[Discover] Replace deprecated page components (#162836)

- Closes https://github.com/elastic/kibana/issues/161426

## Summary

This PR updates usage of Eui page components for the following plugins.
Also took this opportunity to make these pages a little nicer.

### src/plugins/data_view_management

Before:
<img width="600" alt="Screenshot 2023-07-31 at 20 09 28"
src="5b52d31d-b759-40f9-8a8e-d428f6431f45">

After:
<img width="600" alt="Screenshot 2023-07-31 at 20 09 37"
src="dfd738d9-fa19-41d2-a55e-6157d9f2310a">


### examples/search_examples

Before:
<img width="600" alt="Screenshot 2023-07-31 at 17 41 03"
src="cc739f2d-bbaa-4cae-80d8-27599170a40a">

After:
<img width="600" alt="Screenshot 2023-07-31 at 18 01 47"
src="ab596aae-f9b4-48f0-8401-a507f4814624">

Before:
<img width="600" alt="Screenshot 2023-07-31 at 20 30 19"
src="40327fdd-7161-4d65-86ff-ff38455477fb">

After:
<img width="600" alt="Screenshot 2023-07-31 at 20 30 39"
src="8455c09f-6d84-4ad1-9e25-f8555a4f40db">

Before:
<img width="600" alt="Screenshot 2023-07-31 at 18 20 47"
src="68b05f4a-fcc0-4051-92b4-88a34bd22fb6">

After:
<img width="600" alt="Screenshot 2023-07-31 at 18 44 32"
src="1fd00e7e-3663-470f-927a-b7d7a0fe6c20">

### examples/partial_results_example

Before:
<img width="600" alt="Screenshot 2023-07-31 at 17 29 32"
src="589a2d93-c42a-4948-b805-0447e96f8ae4">

After:
<img width="600" alt="Screenshot 2023-07-31 at 17 31 52"
src="22df2b40-a9ec-4e11-b4dc-19da86c73059">

### examples/field_formats_example

Before:
<img width="600" alt="Screenshot 2023-07-31 at 17 27 44"
src="d3fa1fc3-e3e2-4f69-87e5-7187f8d3b000">

After:
<img width="600" alt="Screenshot 2023-07-31 at 17 27 56"
src="5633dba4-85ed-4767-bc8c-dee0c4da8798">

### examples/data_view_field_editor_example

Before:
<img width="600" alt="Screenshot 2023-07-31 at 17 33 15"
src="0be36a57-aef3-4679-ac77-9d5073b72fc2">

After:
<img width="600" alt="Screenshot 2023-07-31 at 17 37 34"
src="e43b0a1d-12fe-4d06-90b0-63cbc36e5ac2">


### Checklist

- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
This commit is contained in:
Julia Rechkunova 2023-08-02 20:08:17 +02:00 committed by GitHub
parent ffb716d8af
commit 148f9d757f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 975 additions and 1031 deletions

View file

@ -13,11 +13,8 @@ import {
EuiFlexGroup, EuiFlexGroup,
EuiFlexItem, EuiFlexItem,
EuiInMemoryTable, EuiInMemoryTable,
EuiPage, EuiPageTemplate,
EuiPageBody, EuiSpacer,
EuiPageContent_Deprecated as EuiPageContent,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiPageHeader,
EuiText, EuiText,
useGeneratedHtmlId, useGeneratedHtmlId,
} from '@elastic/eui'; } from '@elastic/eui';
@ -86,6 +83,7 @@ const DataViewFieldEditorExample = ({ dataView, dataViewFieldEditor }: Props) =>
const content = dataView ? ( const content = dataView ? (
<> <>
<EuiText data-test-subj="dataViewTitle">Data view: {dataView.title}</EuiText> <EuiText data-test-subj="dataViewTitle">Data view: {dataView.title}</EuiText>
<EuiSpacer />
<EuiFlexGroup alignItems="center"> <EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiButton <EuiButton
@ -118,6 +116,7 @@ const DataViewFieldEditorExample = ({ dataView, dataViewFieldEditor }: Props) =>
/> />
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
<EuiSpacer size="s" />
<EuiInMemoryTable<DataViewField> <EuiInMemoryTable<DataViewField>
items={fields} items={fields}
columns={columns} columns={columns}
@ -136,14 +135,10 @@ const DataViewFieldEditorExample = ({ dataView, dataViewFieldEditor }: Props) =>
); );
return ( return (
<EuiPage> <EuiPageTemplate offset={0}>
<EuiPageBody> <EuiPageTemplate.Header pageTitle="Data view field editor demo" />
<EuiPageHeader>Data view field editor demo</EuiPageHeader> <EuiPageTemplate.Section>{content}</EuiPageTemplate.Section>
<EuiPageContent> </EuiPageTemplate>
<EuiPageContentBody>{content}</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
); );
}; };

View file

@ -13,12 +13,7 @@ import {
EuiCode, EuiCode,
EuiCodeBlock, EuiCodeBlock,
EuiLink, EuiLink,
EuiPage, EuiPageTemplate,
EuiPageBody,
EuiPageContent_Deprecated as EuiPageContent,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiPageHeader,
EuiPageHeaderSection,
EuiSpacer, EuiSpacer,
EuiText, EuiText,
EuiTitle, EuiTitle,
@ -61,7 +56,7 @@ const UsingAnExistingFieldFormatExample: React.FC<{ deps: Deps }> = (props) => {
</p> </p>
</EuiText> </EuiText>
<EuiSpacer size={'s'} /> <EuiSpacer size={'s'} />
<EuiCodeBlock>{example1SampleCode}</EuiCodeBlock> <EuiCodeBlock language="jsx">{example1SampleCode}</EuiCodeBlock>
<EuiSpacer size={'s'} /> <EuiSpacer size={'s'} />
<EuiBasicTable <EuiBasicTable
data-test-subj={'example1 sample table'} data-test-subj={'example1 sample table'}
@ -97,11 +92,11 @@ const CreatingCustomFieldFormat: React.FC<{ deps: Deps }> = (props) => {
</p> </p>
</EuiText> </EuiText>
<EuiSpacer size={'s'} /> <EuiSpacer size={'s'} />
<EuiCodeBlock>{example2SampleCodePart1}</EuiCodeBlock> <EuiCodeBlock language="jsx">{example2SampleCodePart1}</EuiCodeBlock>
<EuiSpacer size={'xs'} /> <EuiSpacer size={'xs'} />
<EuiCodeBlock>{example2SampleCodePart2}</EuiCodeBlock> <EuiCodeBlock language="jsx">{example2SampleCodePart2}</EuiCodeBlock>
<EuiSpacer size={'xs'} /> <EuiSpacer size={'xs'} />
<EuiCodeBlock>{example2SampleCodePart3}</EuiCodeBlock> <EuiCodeBlock language="jsx">{example2SampleCodePart3}</EuiCodeBlock>
<EuiSpacer size={'s'} /> <EuiSpacer size={'s'} />
<EuiBasicTable <EuiBasicTable
items={sample} items={sample}
@ -151,7 +146,7 @@ const CreatingCustomFieldFormatEditor: React.FC<{ deps: Deps }> = (props) => {
</p> </p>
</EuiText> </EuiText>
<EuiSpacer size={'s'} /> <EuiSpacer size={'s'} />
<EuiCodeBlock>{example3SampleCode}</EuiCodeBlock> <EuiCodeBlock language="jsx">{example3SampleCode}</EuiCodeBlock>
<EuiSpacer size={'s'} /> <EuiSpacer size={'s'} />
<EuiCallOut <EuiCallOut
@ -173,45 +168,29 @@ const CreatingCustomFieldFormatEditor: React.FC<{ deps: Deps }> = (props) => {
export const App: React.FC<{ deps: Deps }> = (props) => { export const App: React.FC<{ deps: Deps }> = (props) => {
return ( return (
<EuiPage> <EuiPageTemplate offset={0}>
<EuiPageBody style={{ maxWidth: 1200, margin: '0 auto' }}> <EuiPageTemplate.Header pageTitle="Field formats examples" />
<EuiPageHeader> <EuiPageTemplate.Section grow={false}>
<EuiPageHeaderSection> <EuiTitle size="m">
<EuiTitle size="l"> <h2>Using an existing field format</h2>
<h1>Field formats examples</h1> </EuiTitle>
</EuiTitle> <EuiSpacer />
</EuiPageHeaderSection> <UsingAnExistingFieldFormatExample deps={props.deps} />
</EuiPageHeader> </EuiPageTemplate.Section>
<EuiPageContent> <EuiPageTemplate.Section grow={false}>
<EuiPageContentBody style={{ maxWidth: 800, margin: '0 auto' }}> <EuiTitle size="m">
<section> <h2>Creating a custom field format</h2>
<EuiTitle size="m"> </EuiTitle>
<h2>Using an existing field format</h2> <EuiSpacer />
</EuiTitle> <CreatingCustomFieldFormat deps={props.deps} />
<EuiSpacer /> </EuiPageTemplate.Section>
<UsingAnExistingFieldFormatExample deps={props.deps} /> <EuiPageTemplate.Section grow={false}>
</section> <EuiTitle size="m">
<EuiSpacer /> <h2>Creating a custom field format editor</h2>
<EuiSpacer /> </EuiTitle>
<section> <EuiSpacer />
<EuiTitle size="m"> <CreatingCustomFieldFormatEditor deps={props.deps} />
<h2>Creating a custom field format</h2> </EuiPageTemplate.Section>
</EuiTitle> </EuiPageTemplate>
<EuiSpacer />
<CreatingCustomFieldFormat deps={props.deps} />
</section>
<EuiSpacer />
<EuiSpacer />
<section>
<EuiTitle size="m">
<h2>Creating a custom field format editor</h2>
</EuiTitle>
<EuiSpacer />
<CreatingCustomFieldFormatEditor deps={props.deps} />
</section>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
); );
}; };

View file

@ -12,15 +12,9 @@ import {
EuiBasicTable, EuiBasicTable,
EuiCallOut, EuiCallOut,
EuiCodeBlock, EuiCodeBlock,
EuiPage, EuiPageTemplate,
EuiPageBody,
EuiPageContent_Deprecated as EuiPageContent,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiPageHeader,
EuiPageHeaderSection,
EuiSpacer, EuiSpacer,
EuiText, EuiText,
EuiTitle,
} from '@elastic/eui'; } from '@elastic/eui';
import type { Datatable } from '@kbn/expressions-plugin/common'; import type { Datatable } from '@kbn/expressions-plugin/common';
import { ExpressionsContext } from './expressions_context'; import { ExpressionsContext } from './expressions_context';
@ -46,45 +40,35 @@ export function App() {
}, [expressions]); }, [expressions]);
return ( return (
<EuiPage> <EuiPageTemplate offset={0}>
<EuiPageBody style={{ maxWidth: 1200, margin: '0 auto' }}> <EuiPageTemplate.Header pageTitle="Partial Results Demo" />
<EuiPageHeader> <EuiPageTemplate.Section>
<EuiPageHeaderSection> <EuiText data-test-subj="example-help">
<EuiTitle size="l"> <p>
<h1>Partial Results Demo</h1> This example listens for the window events and adds them to the table along with a
</EuiTitle> trigger counter.
</EuiPageHeaderSection> </p>
</EuiPageHeader> </EuiText>
<EuiPageContent> <EuiSpacer size={'m'} />
<EuiPageContentBody style={{ maxWidth: 800, margin: '0 auto' }}> <EuiCodeBlock>{expression}</EuiCodeBlock>
<EuiText data-test-subj="example-help"> <EuiSpacer size={'m'} />
<p> {datatable ? (
This example listens for the window events and adds them to the table along with a <EuiBasicTable
trigger counter. textOnly={true}
</p> data-test-subj={'example-table'}
</EuiText> columns={datatable.columns?.map(({ id: field, name }) => ({
<EuiSpacer size={'m'} /> field,
<EuiCodeBlock>{expression}</EuiCodeBlock> name,
<EuiSpacer size={'m'} /> 'data-test-subj': `example-column-${field.toLowerCase()}`,
{datatable ? ( }))}
<EuiBasicTable items={datatable.rows ?? []}
textOnly={true} />
data-test-subj={'example-table'} ) : (
columns={datatable.columns?.map(({ id: field, name }) => ({ <EuiCallOut color="success">
field, <p>Click or press any key.</p>
name, </EuiCallOut>
'data-test-subj': `example-column-${field.toLowerCase()}`, )}
}))} </EuiPageTemplate.Section>
items={datatable.rows ?? []} </EuiPageTemplate>
/>
) : (
<EuiCallOut color="success">
<p>Click or press any key.</p>
</EuiCallOut>
)}
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
); );
} }

View file

@ -7,7 +7,7 @@
*/ */
import React, { PropsWithChildren } from 'react'; import React, { PropsWithChildren } from 'react';
import { EuiPage, EuiPageSideBar_Deprecated as EuiPageSideBar, EuiSideNav } from '@elastic/eui'; import { EuiPageTemplate, EuiSideNav } from '@elastic/eui';
import { IBasePath } from '@kbn/core/public'; import { IBasePath } from '@kbn/core/public';
import { PLUGIN_ID } from '../../common'; import { PLUGIN_ID } from '../../common';
@ -55,11 +55,11 @@ export const SearchExamplePage: React.FC<Props> = ({
basePath, basePath,
}: PropsWithChildren<Props>) => { }: PropsWithChildren<Props>) => {
return ( return (
<EuiPage> <EuiPageTemplate offset={0}>
<EuiPageSideBar> <EuiPageTemplate.Sidebar>
<SideNav exampleLinks={exampleLinks} basePath={basePath} /> <SideNav exampleLinks={exampleLinks} basePath={basePath} />
</EuiPageSideBar> </EuiPageTemplate.Sidebar>
{children} {children}
</EuiPage> </EuiPageTemplate>
); );
}; };

View file

@ -16,11 +16,7 @@ import {
EuiFlexGrid, EuiFlexGrid,
EuiFlexItem, EuiFlexItem,
EuiFormLabel, EuiFormLabel,
EuiHorizontalRule, EuiPageTemplate,
EuiPageBody,
EuiPageContent_Deprecated as EuiPageContent,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiPageHeader,
EuiProgress, EuiProgress,
EuiSpacer, EuiSpacer,
EuiTabbedContent, EuiTabbedContent,
@ -531,333 +527,322 @@ export const SearchExamplesApp = ({
]; ];
return ( return (
<EuiPageBody> <>
<EuiPageHeader> <EuiPageTemplate.Header
<EuiTitle size="l"> pageTitle={i18n.translate('searchExamples.helloWorldText', {
<h1> defaultMessage: '{name}',
<FormattedMessage values: { name: PLUGIN_NAME },
id="searchExamples.helloWorldText" })}
defaultMessage="{name}" />
values={{ name: PLUGIN_NAME }} <EuiPageTemplate.Section grow={false}>
<navigation.ui.TopNavMenu
appName={PLUGIN_ID}
showSearchBar={true}
useDefaultBehaviors={true}
indexPatterns={dataView ? [dataView] : undefined}
/>
<EuiFlexGrid columns={4}>
<EuiFlexItem>
<EuiFormLabel>Data view</EuiFormLabel>
<IndexPatternSelect
placeholder={i18n.translate('searchSessionExample.selectDataViewPlaceholder', {
defaultMessage: 'Select data view',
})}
indexPatternId={dataView?.id || ''}
onChange={async (dataViewId?: string) => {
if (dataViewId) {
const newDataView = await data.dataViews.get(dataViewId);
setDataView(newDataView);
} else {
setDataView(undefined);
}
}}
isClearable={false}
data-test-subj="dataViewSelector"
/> />
</h1> </EuiFlexItem>
</EuiTitle> <EuiFlexItem>
</EuiPageHeader> <EuiFormLabel>Field (using {bucketAggType} buckets)</EuiFormLabel>
<EuiPageContent> <EuiComboBox
<EuiPageContentBody> options={formatFieldsToComboBox(getAggregatableStrings(fields))}
<navigation.ui.TopNavMenu selectedOptions={formatFieldToComboBox(selectedBucketField)}
appName={PLUGIN_ID} singleSelection={true}
showSearchBar={true} onChange={(option) => {
useDefaultBehaviors={true} if (option.length) {
indexPatterns={dataView ? [dataView] : undefined} const fld = dataView?.getFieldByName(option[0].label);
/> setSelectedBucketField(fld || null);
<EuiFlexGrid columns={4}> } else {
<EuiFlexItem> setSelectedBucketField(null);
<EuiFormLabel>Data view</EuiFormLabel> }
<IndexPatternSelect }}
placeholder={i18n.translate('searchSessionExample.selectDataViewPlaceholder', { sortMatchesBy="startsWith"
defaultMessage: 'Select data view', data-test-subj="searchBucketField"
})} />
indexPatternId={dataView?.id || ''} </EuiFlexItem>
onChange={async (dataViewId?: string) => { <EuiFlexItem>
if (dataViewId) { <EuiFormLabel>Numeric Field (using {metricAggType} metrics)</EuiFormLabel>
const newDataView = await data.dataViews.get(dataViewId); <EuiComboBox
setDataView(newDataView); options={formatFieldsToComboBox(getNumeric(fields))}
} else { selectedOptions={formatFieldToComboBox(selectedNumericField)}
setDataView(undefined); singleSelection={true}
} onChange={(option) => {
}} if (option.length) {
isClearable={false} const fld = dataView?.getFieldByName(option[0].label);
data-test-subj="dataViewSelector" setSelectedNumericField(fld || null);
/> } else {
</EuiFlexItem> setSelectedNumericField(null);
<EuiFlexItem> }
<EuiFormLabel>Field (using {bucketAggType} buckets)</EuiFormLabel> }}
<EuiComboBox sortMatchesBy="startsWith"
options={formatFieldsToComboBox(getAggregatableStrings(fields))} data-test-subj="searchMetricField"
selectedOptions={formatFieldToComboBox(selectedBucketField)} />
singleSelection={true} </EuiFlexItem>
onChange={(option) => { <EuiFlexItem>
if (option.length) { <EuiFormLabel>Fields to queryString</EuiFormLabel>
const fld = dataView?.getFieldByName(option[0].label); <EuiComboBox
setSelectedBucketField(fld || null); options={formatFieldsToComboBox(fields)}
} else { selectedOptions={formatFieldsToComboBox(selectedFields)}
setSelectedBucketField(null); singleSelection={false}
} onChange={(option) => {
}} const flds = option
sortMatchesBy="startsWith" .map((opt) => dataView?.getFieldByName(opt?.label))
data-test-subj="searchBucketField" .filter((f) => f);
/> setSelectedFields(flds.length ? (flds as DataViewField[]) : []);
</EuiFlexItem> }}
<EuiFlexItem> sortMatchesBy="startsWith"
<EuiFormLabel>Numeric Field (using {metricAggType} metrics)</EuiFormLabel> />
<EuiComboBox </EuiFlexItem>
options={formatFieldsToComboBox(getNumeric(fields))} </EuiFlexGrid>
selectedOptions={formatFieldToComboBox(selectedNumericField)}
singleSelection={true}
onChange={(option) => {
if (option.length) {
const fld = dataView?.getFieldByName(option[0].label);
setSelectedNumericField(fld || null);
} else {
setSelectedNumericField(null);
}
}}
sortMatchesBy="startsWith"
data-test-subj="searchMetricField"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormLabel>Fields to queryString</EuiFormLabel>
<EuiComboBox
options={formatFieldsToComboBox(fields)}
selectedOptions={formatFieldsToComboBox(selectedFields)}
singleSelection={false}
onChange={(option) => {
const flds = option
.map((opt) => dataView?.getFieldByName(opt?.label))
.filter((f) => f);
setSelectedFields(flds.length ? (flds as DataViewField[]) : []);
}}
sortMatchesBy="startsWith"
/>
</EuiFlexItem>
</EuiFlexGrid>
<EuiHorizontalRule /> <EuiSpacer size="xl" />
<EuiFlexGrid columns={2}> <EuiFlexGrid columns={2}>
<EuiFlexItem style={{ width: '40%' }}> <EuiFlexItem>
<EuiTitle size="s">
<h3>
Searching Elasticsearch using <EuiCode>data.search</EuiCode>
</h3>
</EuiTitle>
<EuiText>
If you want to fetch data from Elasticsearch, you can use the different services
provided by the <EuiCode>data</EuiCode> plugin. These help you get the data view and
search bar configuration, format them into a DSL query and send it to Elasticsearch.
<EuiSpacer /> <EuiSpacer />
<EuiTitle size="s"> <EuiButtonEmpty size="xs" onClick={onClickHandler} iconType="play">
<h3> <FormattedMessage
Searching Elasticsearch using <EuiCode>data.search</EuiCode> id="searchExamples.buttonText"
</h3> defaultMessage="Request from low-level client (data.search.search)."
</EuiTitle>
<EuiText>
If you want to fetch data from Elasticsearch, you can use the different services
provided by the <EuiCode>data</EuiCode> plugin. These help you get the data view and
search bar configuration, format them into a DSL query and send it to Elasticsearch.
<EuiSpacer />
<EuiButtonEmpty size="xs" onClick={onClickHandler} iconType="play">
<FormattedMessage
id="searchExamples.buttonText"
defaultMessage="Request from low-level client (data.search.search)."
/>
</EuiButtonEmpty>
<EuiText size="xs" color="subdued" className="searchExampleStepDsc">
<FormattedMessage
id="searchExamples.buttonText"
defaultMessage="Metrics aggregation with raw documents in response."
/>
</EuiText>
<EuiButtonEmpty
size="xs"
onClick={() => onSearchSourceClickHandler(true, true)}
iconType="play"
data-test-subj="searchSourceWithOther"
>
<FormattedMessage
id="searchExamples.searchSource.buttonText"
defaultMessage="Request from high-level client (data.search.searchSource)"
/>
</EuiButtonEmpty>
<EuiText size="xs" color="subdued" className="searchExampleStepDsc">
<FormattedMessage
id="searchExamples.buttonText"
defaultMessage="Bucket and metrics aggregations, with other bucket and default warnings."
/>
</EuiText>
<EuiButtonEmpty
size="xs"
onClick={() => onSearchSourceClickHandler(false, false)}
iconType="play"
data-test-subj="searchSourceWithoutOther"
>
<FormattedMessage
id="searchExamples.searchSource.buttonText"
defaultMessage="Request from high-level client (data.search.searchSource)"
/>
</EuiButtonEmpty>
<EuiText size="xs" color="subdued" className="searchExampleStepDsc">
<FormattedMessage
id="searchExamples.buttonText"
defaultMessage="Bucket and metrics aggregations, without other bucket and with custom logic to handle warnings."
/>
</EuiText>
</EuiText>
<EuiSpacer />
<EuiTitle size="xs">
<h3>Handling errors & warnings</h3>
</EuiTitle>
<EuiText>
When fetching data from Elasticsearch, there are several different ways warnings and
errors may be returned. In general, it is recommended to surface these in the UX.
<EuiSpacer />
<EuiButtonEmpty
size="xs"
onClick={onWarningSearchClickHandler}
iconType="play"
data-test-subj="searchWithWarning"
>
<FormattedMessage
id="searchExamples.searchWithWarningButtonText"
defaultMessage="Request with a warning in response"
/>
</EuiButtonEmpty>
<EuiText />
<EuiButtonEmpty
size="xs"
onClick={onErrorSearchClickHandler}
iconType="play"
data-test-subj="searchWithError"
>
<FormattedMessage
id="searchExamples.searchWithErrorButtonText"
defaultMessage="Request with an error in response"
/>
</EuiButtonEmpty>
</EuiText>
<EuiSpacer />
<EuiTitle size="xs">
<h3>Handling partial results</h3>
</EuiTitle>
<EuiText>
The observable returned from <EuiCode>data.search</EuiCode> provides partial results
when the response is not yet complete. These can be handled to update a chart or
simply a progress bar:
<EuiSpacer />
<EuiCodeBlock language="html" fontSize="s" paddingSize="s" overflowHeight={450}>
&lt;EuiProgress value=&#123;response.loaded&#125; max=&#123;response.total&#125;
/&gt;
</EuiCodeBlock>
Below is an example showing a custom search strategy that emits partial Fibonacci
sequences up to the length provided, updates the response with each partial result,
and updates a progress bar (see the Response tab).
<EuiFieldNumber
id="FibonacciN"
placeholder="Number of Fibonacci numbers to generate"
value={fibonacciN}
onChange={(event) => setFibonacciN(parseInt(event.target.value, 10))}
/> />
<EuiButtonEmpty </EuiButtonEmpty>
size="xs" <EuiText size="xs" color="subdued" className="searchExampleStepDsc">
onClick={onPartialResultsClickHandler} <FormattedMessage
iconType="play" id="searchExamples.buttonText"
data-test-subj="requestFibonacci" defaultMessage="Metrics aggregation with raw documents in response."
>
Request Fibonacci sequence
</EuiButtonEmpty>
</EuiText>
<EuiSpacer />
<EuiTitle size="s">
<h3>Writing a custom search strategy</h3>
</EuiTitle>
<EuiText>
If you want to do some pre or post processing on the server, you might want to
create a custom search strategy. This example uses such a strategy, passing in
custom input and receiving custom output back.
<EuiSpacer />
<EuiCheckbox
id="GetCool"
label={
<FormattedMessage
id="searchExamples.getCoolCheckbox"
defaultMessage="Get cool parameter?"
/>
}
checked={getCool}
onChange={(event) => setGetCool(event.target.checked)}
/> />
<EuiButtonEmpty size="xs" onClick={onMyStrategyClickHandler} iconType="play">
<FormattedMessage
id="searchExamples.myStrategyButtonText"
defaultMessage="Request from low-level client via My Strategy"
/>
</EuiButtonEmpty>
</EuiText> </EuiText>
<EuiButtonEmpty
size="xs"
onClick={() => onSearchSourceClickHandler(true, true)}
iconType="play"
data-test-subj="searchSourceWithOther"
>
<FormattedMessage
id="searchExamples.searchSource.buttonText"
defaultMessage="Request from high-level client (data.search.searchSource)"
/>
</EuiButtonEmpty>
<EuiText size="xs" color="subdued" className="searchExampleStepDsc">
<FormattedMessage
id="searchExamples.buttonText"
defaultMessage="Bucket and metrics aggregations, with other bucket and default warnings."
/>
</EuiText>
<EuiButtonEmpty
size="xs"
onClick={() => onSearchSourceClickHandler(false, false)}
iconType="play"
data-test-subj="searchSourceWithoutOther"
>
<FormattedMessage
id="searchExamples.searchSource.buttonText"
defaultMessage="Request from high-level client (data.search.searchSource)"
/>
</EuiButtonEmpty>
<EuiText size="xs" color="subdued" className="searchExampleStepDsc">
<FormattedMessage
id="searchExamples.buttonText"
defaultMessage="Bucket and metrics aggregations, without other bucket and with custom logic to handle warnings."
/>
</EuiText>
</EuiText>
<EuiSpacer />
<EuiTitle size="xs">
<h3>Handling errors & warnings</h3>
</EuiTitle>
<EuiText>
When fetching data from Elasticsearch, there are several different ways warnings and
errors may be returned. In general, it is recommended to surface these in the UX.
<EuiSpacer /> <EuiSpacer />
<EuiTitle size="s"> <EuiButtonEmpty
<h3>Client side search session caching</h3> size="xs"
</EuiTitle> onClick={onWarningSearchClickHandler}
<EuiText> iconType="play"
<EuiButtonEmpty data-test-subj="searchWithWarning"
size="xs" >
onClick={() => data.search.session.start()} <FormattedMessage
iconType="warning" id="searchExamples.searchWithWarningButtonText"
data-test-subj="searchExamplesStartSession" defaultMessage="Request with a warning in response"
> />
<FormattedMessage </EuiButtonEmpty>
id="searchExamples.startNewSession" <EuiText />
defaultMessage="Start a new session" <EuiButtonEmpty
/> size="xs"
</EuiButtonEmpty> onClick={onErrorSearchClickHandler}
<EuiButtonEmpty iconType="play"
size="xs" data-test-subj="searchWithError"
onClick={() => data.search.session.clear()} >
iconType="warning" <FormattedMessage
data-test-subj="searchExamplesClearSession" id="searchExamples.searchWithErrorButtonText"
> defaultMessage="Request with an error in response"
<FormattedMessage />
id="searchExamples.clearSession" </EuiButtonEmpty>
defaultMessage="Clear session" </EuiText>
/> <EuiSpacer />
</EuiButtonEmpty> <EuiTitle size="xs">
<EuiButtonEmpty <h3>Handling partial results</h3>
size="xs" </EuiTitle>
onClick={onClientSideSessionCacheClickHandler} <EuiText>
iconType="play" The observable returned from <EuiCode>data.search</EuiCode> provides partial results
data-test-subj="searchExamplesCacheSearch" when the response is not yet complete. These can be handled to update a chart or
> simply a progress bar:
<FormattedMessage
id="searchExamples.myStrategyButtonText"
defaultMessage="Request from low-level client via My Strategy"
/>
</EuiButtonEmpty>
</EuiText>
<EuiSpacer /> <EuiSpacer />
<EuiTitle size="s"> <EuiCodeBlock language="html" fontSize="s" paddingSize="s" overflowHeight={450}>
<h3>Using search on the server</h3> &lt;EuiProgress value=&#123;response.loaded&#125; max=&#123;response.total&#125;
</EuiTitle> /&gt;
<EuiText> </EuiCodeBlock>
You can also run your search request from the server, without registering a search Below is an example showing a custom search strategy that emits partial Fibonacci
strategy. This request does not take the configuration of{' '} sequences up to the length provided, updates the response with each partial result,
<EuiCode>TopNavMenu</EuiCode> into account, but you could pass those down to the and updates a progress bar (see the Response tab).
server as well. <EuiFieldNumber
<br /> id="FibonacciN"
When executing search on the server, make sure to cancel the search in case user placeholder="Number of Fibonacci numbers to generate"
cancels corresponding network request. This could happen in case user re-runs a value={fibonacciN}
query or leaves the page without waiting for the result. Cancellation API is similar onChange={(event) => setFibonacciN(parseInt(event.target.value, 10))}
on client and server and use `AbortController`.
<EuiSpacer />
<EuiButtonEmpty size="xs" onClick={onServerClickHandler} iconType="play">
<FormattedMessage
id="searchExamples.myServerButtonText"
defaultMessage="Request from low-level client on the server"
/>
</EuiButtonEmpty>
</EuiText>
</EuiFlexItem>
<EuiFlexItem style={{ width: '60%' }}>
<EuiTabbedContent
tabs={reqTabs}
selectedTab={reqTabs[selectedTab]}
onTabClick={(tab) => setSelectedTab(reqTabs.indexOf(tab))}
/> />
<EuiButtonEmpty
size="xs"
onClick={onPartialResultsClickHandler}
iconType="play"
data-test-subj="requestFibonacci"
>
Request Fibonacci sequence
</EuiButtonEmpty>
</EuiText>
<EuiSpacer />
<EuiTitle size="s">
<h3>Writing a custom search strategy</h3>
</EuiTitle>
<EuiText>
If you want to do some pre or post processing on the server, you might want to create
a custom search strategy. This example uses such a strategy, passing in custom input
and receiving custom output back.
<EuiSpacer /> <EuiSpacer />
{currentAbortController && isLoading && ( <EuiCheckbox
<EuiButtonEmpty size="xs" onClick={() => currentAbortController?.abort()}> id="GetCool"
label={
<FormattedMessage <FormattedMessage
id="searchExamples.abortButtonText" id="searchExamples.getCoolCheckbox"
defaultMessage="Abort request" defaultMessage="Get cool parameter?"
/> />
</EuiButtonEmpty> }
)} checked={getCool}
</EuiFlexItem> onChange={(event) => setGetCool(event.target.checked)}
</EuiFlexGrid> />
</EuiPageContentBody> <EuiButtonEmpty size="xs" onClick={onMyStrategyClickHandler} iconType="play">
</EuiPageContent> <FormattedMessage
</EuiPageBody> id="searchExamples.myStrategyButtonText"
defaultMessage="Request from low-level client via My Strategy"
/>
</EuiButtonEmpty>
</EuiText>
<EuiSpacer />
<EuiTitle size="s">
<h3>Client side search session caching</h3>
</EuiTitle>
<EuiText>
<EuiButtonEmpty
size="xs"
onClick={() => data.search.session.start()}
iconType="warning"
data-test-subj="searchExamplesStartSession"
>
<FormattedMessage
id="searchExamples.startNewSession"
defaultMessage="Start a new session"
/>
</EuiButtonEmpty>
<EuiButtonEmpty
size="xs"
onClick={() => data.search.session.clear()}
iconType="warning"
data-test-subj="searchExamplesClearSession"
>
<FormattedMessage id="searchExamples.clearSession" defaultMessage="Clear session" />
</EuiButtonEmpty>
<EuiButtonEmpty
size="xs"
onClick={onClientSideSessionCacheClickHandler}
iconType="play"
data-test-subj="searchExamplesCacheSearch"
>
<FormattedMessage
id="searchExamples.myStrategyButtonText"
defaultMessage="Request from low-level client via My Strategy"
/>
</EuiButtonEmpty>
</EuiText>
<EuiSpacer />
<EuiTitle size="s">
<h3>Using search on the server</h3>
</EuiTitle>
<EuiText>
You can also run your search request from the server, without registering a search
strategy. This request does not take the configuration of{' '}
<EuiCode>TopNavMenu</EuiCode> into account, but you could pass those down to the
server as well.
<br />
When executing search on the server, make sure to cancel the search in case user
cancels corresponding network request. This could happen in case user re-runs a query
or leaves the page without waiting for the result. Cancellation API is similar on
client and server and use `AbortController`.
<EuiSpacer />
<EuiButtonEmpty size="xs" onClick={onServerClickHandler} iconType="play">
<FormattedMessage
id="searchExamples.myServerButtonText"
defaultMessage="Request from low-level client on the server"
/>
</EuiButtonEmpty>
</EuiText>
</EuiFlexItem>
<EuiFlexItem style={{ width: '60%' }}>
<EuiTabbedContent
tabs={reqTabs}
selectedTab={reqTabs[selectedTab]}
onTabClick={(tab) => setSelectedTab(reqTabs.indexOf(tab))}
/>
<EuiSpacer />
{currentAbortController && isLoading && (
<EuiButtonEmpty size="xs" onClick={() => currentAbortController?.abort()}>
<FormattedMessage
id="searchExamples.abortButtonText"
defaultMessage="Abort request"
/>
</EuiButtonEmpty>
)}
</EuiFlexItem>
</EuiFlexGrid>
</EuiPageTemplate.Section>
</>
); );
}; };

View file

@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n';
import useObservable from 'react-use/lib/useObservable'; import useObservable from 'react-use/lib/useObservable';
import { import {
EuiAccordion, EuiAccordion,
EuiButton,
EuiButtonEmpty, EuiButtonEmpty,
EuiCallOut, EuiCallOut,
EuiCode, EuiCode,
@ -20,11 +21,7 @@ import {
EuiFlexItem, EuiFlexItem,
EuiFormLabel, EuiFormLabel,
EuiLoadingSpinner, EuiLoadingSpinner,
EuiPageBody, EuiPageTemplate,
EuiPageContent_Deprecated as EuiPageContent,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiPageHeader,
EuiPageHeaderSection,
EuiSpacer, EuiSpacer,
EuiText, EuiText,
EuiTitle, EuiTitle,
@ -228,49 +225,43 @@ export const SearchSessionsExampleApp = ({
}, [search, state.restoreSessionId]); }, [search, state.restoreSessionId]);
return ( return (
<EuiPageBody> <>
<EuiPageHeader> <EuiPageTemplate.Header pageTitle="Search session example" />
<EuiPageHeaderSection> <EuiPageTemplate.Section grow={false}>
<EuiTitle size="l"> {!isShardDelayEnabled(data) && (
<h1>Search session example</h1> <>
</EuiTitle> <NoShardDelayCallout />
<EuiSpacer /> <EuiSpacer />
{!isShardDelayEnabled(data) && ( </>
<> )}
<NoShardDelayCallout /> {!dataView && (
<EuiSpacer /> <>
</> <NoDataViewsCallout />
)} <EuiSpacer />
{!dataView && ( </>
<> )}
<NoDataViewsCallout /> <EuiText>
<EuiSpacer /> <p>
</> This example shows how you can use <EuiCode>data.search.session</EuiCode> service to
)} group your searches into a search session and allow user to save search results for
<EuiText> later. <br />
<p> Start a long-running search, save the session and then restore it. See how fast search
This example shows how you can use <EuiCode>data.search.session</EuiCode> service to is completed when restoring the session comparing to when doing initial search. <br />
group your searches into a search session and allow user to save search results for <br />
later. <br /> Follow this demo step-by-step:{' '}
Start a long-running search, save the session and then restore it. See how fast search <b>configure the query, start the search and then save your session.</b> You can save
is completed when restoring the session comparing to when doing initial search. <br /> your session both when search is still in progress or when it is completed. After you
<br /> save the session and when initial search is completed you can <b>restore the session</b>
Follow this demo step-by-step:{' '} : the search will re-run reusing previous results. It will finish a lot faster then the
<b>configure the query, start the search and then save your session.</b> You can save initial search. You can also <b>go to search sessions management</b> and{' '}
your session both when search is still in progress or when it is completed. After you <b>get back to the stored results</b> from there.
save the session and when initial search is completed you can{' '} </p>
<b>restore the session</b>: the search will re-run reusing previous results. It will </EuiText>
finish a lot faster then the initial search. You can also{' '} </EuiPageTemplate.Section>
<b>go to search sessions management</b> and <b>get back to the stored results</b> from <>
there. {!isRestoring && (
</p> <>
</EuiText> <EuiPageTemplate.Section grow={false}>
</EuiPageHeaderSection>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>
{!isRestoring && (
<>
<EuiTitle size="s"> <EuiTitle size="s">
<h2>1. Configure the search query (OK to leave defaults)</h2> <h2>1. Configure the search query (OK to leave defaults)</h2>
</EuiTitle> </EuiTitle>
@ -313,22 +304,24 @@ export const SearchSessionsExampleApp = ({
/> />
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
<EuiSpacer size={'xl'} /> </EuiPageTemplate.Section>
<EuiPageTemplate.Section grow={false}>
<EuiTitle size="s"> <EuiTitle size="s">
<h2> <h2>
2. Start the search using <EuiCode>data.search</EuiCode> service 2. Start the search using <EuiCode>data.search</EuiCode> service
</h2> </h2>
</EuiTitle> </EuiTitle>
<EuiText style={{ maxWidth: 600 }}> <EuiSpacer size="s" />
<EuiText>
In this example each search creates a new session by calling{' '} In this example each search creates a new session by calling{' '}
<EuiCode>data.search.session.start()</EuiCode> that returns a{' '} <EuiCode>data.search.session.start()</EuiCode> that returns a{' '}
<EuiCode>searchSessionId</EuiCode>. Then this <EuiCode>searchSessionId</EuiCode> is <EuiCode>searchSessionId</EuiCode>. Then this <EuiCode>searchSessionId</EuiCode> is
passed into a search request. passed into a search request.
<EuiSpacer />
<div> <div>
{demoStep === DemoStep.ConfigureQuery && ( {demoStep === DemoStep.ConfigureQuery && (
<EuiButtonEmpty <EuiButtonEmpty
size="xs" flush="both"
onClick={() => search()} onClick={() => search()}
iconType="play" iconType="play"
disabled={isSearching || !dataView || !numericFieldName} disabled={isSearching || !dataView || !numericFieldName}
@ -337,148 +330,170 @@ export const SearchSessionsExampleApp = ({
Start the search from low-level client (data.search.search) Start the search from low-level client (data.search.search)
</EuiButtonEmpty> </EuiButtonEmpty>
)} )}
{isSearching && <EuiLoadingSpinner />}
{isSearching && (
<>
<EuiSpacer />
<EuiLoadingSpinner />
</>
)}
{response && request && ( {response && request && (
<SearchInspector <>
accordionId={'1'} <EuiSpacer />
request={request} <SearchInspector
response={response} accordionId={'1'}
tookMs={tookMs} request={request}
/> response={response}
tookMs={tookMs}
/>
</>
)} )}
</div> </div>
</EuiText> </EuiText>
<EuiSpacer size={'xl'} /> </EuiPageTemplate.Section>
{(demoStep === DemoStep.RunSession ||
demoStep === DemoStep.RestoreSessionOnScreen ||
demoStep === DemoStep.SaveSession) && (
<>
<EuiTitle size="s">
<h2>3. Save your session</h2>
</EuiTitle>
<EuiText style={{ maxWidth: 600 }}>
Use the search session indicator in the Kibana header to save the search
session.
<div>
<EuiButtonEmpty
size="xs"
iconType={'save'}
onClick={() => {
// hack for demo purposes:
document
.querySelector('[data-test-subj="searchSessionIndicator"]')
?.querySelector('button')
?.click();
}}
isDisabled={
demoStep === DemoStep.RestoreSessionOnScreen ||
demoStep === DemoStep.SaveSession
}
>
Try saving the session using the search session indicator in the header.
</EuiButtonEmpty>
</div>
</EuiText>
</>
)}
{(demoStep === DemoStep.RestoreSessionOnScreen ||
demoStep === DemoStep.SaveSession) && (
<>
<EuiSpacer size={'xl'} />
<EuiTitle size="s">
<h2>4. Restore the session</h2>
</EuiTitle>
<EuiText style={{ maxWidth: 600 }}>
Now you can restore your saved session. The same search request completes
significantly faster because it reuses stored results.
<EuiSpacer />
<div>
{!isSearching && !restoreResponse && (
<EuiButtonEmpty
size="xs"
iconType={'refresh'}
onClick={() => {
search(data.search.session.getSessionId());
}}
data-test-subj={'restoreSearch'}
>
Restore the search session
</EuiButtonEmpty>
)}
{isSearching && <EuiLoadingSpinner />}
{restoreRequest && restoreResponse && ( {(demoStep === DemoStep.RunSession ||
demoStep === DemoStep.RestoreSessionOnScreen ||
demoStep === DemoStep.SaveSession) && (
<EuiPageTemplate.Section grow={false}>
<EuiTitle size="s">
<h2>3. Save your session</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText>
Use the search session indicator in the Kibana header to save the search session.
<div>
<EuiButtonEmpty
flush="both"
iconType="save"
onClick={() => {
// hack for demo purposes:
document
.querySelector('[data-test-subj="searchSessionIndicator"]')
?.querySelector('button')
?.click();
}}
isDisabled={
demoStep === DemoStep.RestoreSessionOnScreen ||
demoStep === DemoStep.SaveSession
}
>
Try saving the session using the search session indicator in the header.
</EuiButtonEmpty>
</div>
</EuiText>
</EuiPageTemplate.Section>
)}
{(demoStep === DemoStep.RestoreSessionOnScreen ||
demoStep === DemoStep.SaveSession) && (
<EuiPageTemplate.Section grow={false}>
<EuiTitle size="s">
<h2>4. Restore the session</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText>
Now you can restore your saved session. The same search request completes
significantly faster because it reuses stored results.
<div>
{!isSearching && !restoreResponse && (
<EuiButtonEmpty
flush="both"
iconType="refresh"
onClick={() => {
search(data.search.session.getSessionId());
}}
data-test-subj={'restoreSearch'}
>
Restore the search session
</EuiButtonEmpty>
)}
{isSearching && (
<>
<EuiSpacer />
<EuiLoadingSpinner />
</>
)}
{restoreRequest && restoreResponse && (
<>
<EuiSpacer />
<SearchInspector <SearchInspector
accordionId={'2'} accordionId={'2'}
request={restoreRequest} request={restoreRequest}
response={restoreResponse} response={restoreResponse}
tookMs={restoreTookMs} tookMs={restoreTookMs}
/> />
)} </>
</div> )}
</EuiText> </div>
</> </EuiText>
)} </EuiPageTemplate.Section>
{demoStep === DemoStep.RestoreSessionOnScreen && ( )}
<>
<EuiSpacer size={'xl'} />
<EuiTitle size="s">
<h2>5. Restore from Management</h2>
</EuiTitle>
<EuiText style={{ maxWidth: 600 }}>
You can also get back to your session from the Search Session Management.
<div>
<EuiButtonEmpty
size="xs"
onClick={() => {
// hack for demo purposes:
document
.querySelector('[data-test-subj="searchSessionIndicator"]')
?.querySelector('button')
?.click();
}}
>
Use Search Session indicator to navigate to management
</EuiButtonEmpty>
</div>
</EuiText>
</>
)}
</>
)}
{isRestoring && (
<>
<EuiTitle size="s">
<h2>You restored the search session!</h2>
</EuiTitle>
<EuiSpacer />
<EuiText style={{ maxWidth: 600 }}>
{isSearching && <EuiLoadingSpinner />}
{restoreRequest && restoreResponse && ( {demoStep === DemoStep.RestoreSessionOnScreen && (
<SearchInspector <EuiPageTemplate.Section grow={false}>
accordionId={'2'} <EuiTitle size="s">
request={restoreRequest} <h2>5. Restore from Management</h2>
response={restoreResponse} </EuiTitle>
tookMs={restoreTookMs} <EuiSpacer size="s" />
/> <EuiText>
)} You can also get back to your session from the Search Session Management.
</EuiText> <div>
</> <EuiButtonEmpty
)} flush="both"
<EuiSpacer size={'xl'} /> iconType="refresh"
<EuiButtonEmpty onClick={() => {
// hack for demo purposes:
document
.querySelector('[data-test-subj="searchSessionIndicator"]')
?.querySelector('button')
?.click();
}}
>
Use Search Session indicator to navigate to management
</EuiButtonEmpty>
</div>
</EuiText>
</EuiPageTemplate.Section>
)}
</>
)}
{isRestoring && (
<EuiPageTemplate.Section grow={false}>
<EuiTitle size="s">
<h2>You restored the search session!</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText>
{isSearching && <EuiLoadingSpinner />}
{restoreRequest && restoreResponse && (
<SearchInspector
accordionId={'2'}
request={restoreRequest}
response={restoreResponse}
tookMs={restoreTookMs}
/>
)}
</EuiText>
</EuiPageTemplate.Section>
)}
<EuiPageTemplate.Section>
<EuiButton
onClick={() => { onClick={() => {
// hack to quickly reset all the state and remove state stuff from the URL // hack to quickly reset all the state and remove state stuff from the URL
window.location.assign(window.location.href.split('?')[0]); window.location.assign(window.location.href.split('?')[0]);
}} }}
> >
Start again Start again
</EuiButtonEmpty> </EuiButton>
</EuiPageContentBody> </EuiPageTemplate.Section>
</EuiPageContent> </>
</EuiPageBody> </>
); );
}; };
@ -770,7 +785,9 @@ function NoShardDelayCallout() {
queries. <br /> queries. <br />
We recommend to enable it in your <EuiCode>kibana.dev.yml</EuiCode>: We recommend to enable it in your <EuiCode>kibana.dev.yml</EuiCode>:
</p> </p>
<EuiCodeBlock isCopyable={true}>data.search.aggs.shardDelay.enabled: true</EuiCodeBlock> <EuiCodeBlock isCopyable={true} language="yaml">
data.search.aggs.shardDelay.enabled: true
</EuiCodeBlock>
</EuiCallOut> </EuiCallOut>
); );
} }

View file

@ -13,15 +13,12 @@ import {
EuiFlexGroup, EuiFlexGroup,
EuiFlexItem, EuiFlexItem,
EuiForm, EuiForm,
EuiPageBody, EuiPageTemplate,
EuiPageContent_Deprecated as EuiPageContent,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiPageHeader,
EuiPanel, EuiPanel,
EuiSuperUpdateButton, EuiSuperUpdateButton,
EuiText, EuiText,
EuiTextArea, EuiTextArea,
EuiTitle, EuiSpacer,
} from '@elastic/eui'; } from '@elastic/eui';
import { CoreStart } from '@kbn/core/public'; import { CoreStart } from '@kbn/core/public';
@ -87,78 +84,74 @@ export const SqlSearchExampleApp = ({ notifications, data }: SearchExamplesAppDe
}; };
return ( return (
<EuiPageBody> <>
<EuiPageHeader> <EuiPageTemplate.Header pageTitle="SQL search example" />
<EuiTitle size="l"> <EuiPageTemplate.Section grow={false}>
<h1>SQL search example</h1> <EuiForm>
</EuiTitle> <EuiFlexGroup>
</EuiPageHeader> <EuiFlexItem grow>
<EuiPageContent> <EuiTextArea
<EuiPageContentBody> placeholder="SELECT * FROM library ORDER BY page_count DESC"
<EuiForm> aria-label="SQL query to run"
<EuiFlexGroup> value={sqlQuery}
<EuiFlexItem grow> onChange={(e) => setSqlQuery(e.target.value)}
<EuiTextArea fullWidth
placeholder="SELECT * FROM library ORDER BY page_count DESC" data-test-subj="sqlQueryInput"
aria-label="SQL query to run" />
value={sqlQuery}
onChange={(e) => setSqlQuery(e.target.value)}
fullWidth
data-test-subj="sqlQueryInput"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSuperUpdateButton
isLoading={isLoading}
isDisabled={sqlQuery.length === 0}
onClick={doSearch}
fill={true}
data-test-subj="querySubmitButton"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiForm>
<EuiFlexGroup gutterSize="l">
<EuiFlexItem grow style={{ minWidth: 0 }}>
<EuiPanel grow>
<EuiText>
<h3>Request</h3>
</EuiText>
<EuiCodeBlock
language="json"
fontSize="s"
paddingSize="s"
overflowHeight={720}
isCopyable
data-test-subj="requestCodeBlock"
isVirtualized
>
{JSON.stringify(request, null, 2)}
</EuiCodeBlock>
</EuiPanel>
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow style={{ minWidth: 0 }}> <EuiFlexItem grow={false}>
<EuiPanel grow> <EuiSuperUpdateButton
<EuiText> isLoading={isLoading}
<h3>Response</h3> isDisabled={sqlQuery.length === 0}
</EuiText> onClick={doSearch}
<EuiCodeBlock fill={true}
language="json" data-test-subj="querySubmitButton"
fontSize="s" />
paddingSize="s"
isCopyable
data-test-subj="responseCodeBlock"
overflowHeight={720}
isVirtualized
>
{JSON.stringify(rawResponse, null, 2)}
</EuiCodeBlock>
</EuiPanel>
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
</EuiPageContentBody> </EuiForm>
</EuiPageContent>
</EuiPageBody> <EuiSpacer />
<EuiFlexGroup gutterSize="l">
<EuiFlexItem grow style={{ minWidth: 0 }}>
<EuiPanel grow hasShadow={false} hasBorder>
<EuiText>
<h3>Request</h3>
</EuiText>
<EuiCodeBlock
language="json"
fontSize="s"
paddingSize="s"
overflowHeight={720}
isCopyable
data-test-subj="requestCodeBlock"
isVirtualized
>
{JSON.stringify(request, null, 2)}
</EuiCodeBlock>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem grow style={{ minWidth: 0 }}>
<EuiPanel grow hasShadow={false} hasBorder>
<EuiText>
<h3>Response</h3>
</EuiText>
<EuiCodeBlock
language="json"
fontSize="s"
paddingSize="s"
isCopyable
data-test-subj="responseCodeBlock"
overflowHeight={720}
isVirtualized
>
{JSON.stringify(rawResponse, null, 2)}
</EuiCodeBlock>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageTemplate.Section>
</>
); );
}; };

View file

@ -1,218 +1,213 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EmptyIndexListPrompt should render normally 1`] = ` exports[`EmptyIndexListPrompt should render normally 1`] = `
<Fragment> <EuiPanel
<EuiPageContent_Deprecated className="inpEmptyState"
className="inpEmptyState" color="subdued"
color="subdued" data-test-subj="indexPatternEmptyState"
data-test-subj="indexPatternEmptyState" hasShadow={false}
grow={false} paddingSize="xl"
horizontalPosition="center" >
verticalPosition="center" <EuiPageHeader>
> <EuiTitle>
<EuiPageContentHeader_Deprecated> <h2>
<EuiPageContentHeaderSection_Deprecated> <FormattedMessage
<EuiTitle> defaultMessage="Ready to try Kibana? First, you need data."
<h2> id="indexPatternManagement.createDataView.emptyState.noDataTitle"
values={Object {}}
/>
</h2>
</EuiTitle>
</EuiPageHeader>
<EuiSpacer
size="xl"
/>
<div>
<EuiFlexGrid
className="inpEmptyState__cardGrid"
columns={3}
responsive={true}
>
<EuiFlexItem>
<EuiCard
className="inpEmptyState__card"
description={
<FormattedMessage <FormattedMessage
defaultMessage="Ready to try Kibana? First, you need data." defaultMessage="Add data from a variety of sources."
id="indexPatternManagement.createDataView.emptyState.noDataTitle" id="indexPatternManagement.createDataView.emptyState.integrationCardDescription"
values={Object {}} values={Object {}}
/> />
</h2> }
</EuiTitle> icon={
</EuiPageContentHeaderSection_Deprecated> <EuiIcon
</EuiPageContentHeader_Deprecated> color="subdued"
size="xl"
type="database"
/>
}
onClick={[Function]}
title={
<FormattedMessage
defaultMessage="Add integration"
id="indexPatternManagement.createDataView.emptyState.integrationCardTitle"
values={Object {}}
/>
}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
className="inpEmptyState__card"
description={
<FormattedMessage
defaultMessage="Import a CSV, NDJSON, or log file."
id="indexPatternManagement.createDataView.emptyState.uploadCardDescription"
values={Object {}}
/>
}
icon={
<EuiIcon
color="subdued"
size="xl"
type="document"
/>
}
onClick={[Function]}
title={
<FormattedMessage
defaultMessage="Upload a file"
id="indexPatternManagement.createDataView.emptyState.uploadCardTitle"
values={Object {}}
/>
}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
className="inpEmptyState__card"
description={
<FormattedMessage
defaultMessage="Load a data set and a Kibana dashboard."
id="indexPatternManagement.createDataView.emptyState.sampleDataCardDescription"
values={Object {}}
/>
}
icon={
<EuiIcon
color="subdued"
size="xl"
type="heatmap"
/>
}
onClick={[Function]}
title={
<FormattedMessage
defaultMessage="Add sample data"
id="indexPatternManagement.createDataView.emptyState.sampleDataCardTitle"
values={Object {}}
/>
}
/>
</EuiFlexItem>
</EuiFlexGrid>
<EuiSpacer <EuiSpacer
size="m" size="xxl"
/> />
<EuiPageContentBody_Deprecated> <div
<EuiFlexGrid className="inpEmptyState__footer"
className="inpEmptyState__cardGrid" >
columns={3} <EuiFlexGroup
responsive={true} justifyContent="center"
> >
<EuiFlexItem> <EuiFlexItem
<EuiCard className="inpEmptyState__footerFlexItem"
className="inpEmptyState__card" grow={false}
description={
<FormattedMessage
defaultMessage="Add data from a variety of sources."
id="indexPatternManagement.createDataView.emptyState.integrationCardDescription"
values={Object {}}
/>
}
icon={
<EuiIcon
color="subdued"
size="xl"
type="database"
/>
}
onClick={[Function]}
title={
<FormattedMessage
defaultMessage="Add integration"
id="indexPatternManagement.createDataView.emptyState.integrationCardTitle"
values={Object {}}
/>
}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
className="inpEmptyState__card"
description={
<FormattedMessage
defaultMessage="Import a CSV, NDJSON, or log file."
id="indexPatternManagement.createDataView.emptyState.uploadCardDescription"
values={Object {}}
/>
}
icon={
<EuiIcon
color="subdued"
size="xl"
type="document"
/>
}
onClick={[Function]}
title={
<FormattedMessage
defaultMessage="Upload a file"
id="indexPatternManagement.createDataView.emptyState.uploadCardTitle"
values={Object {}}
/>
}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
className="inpEmptyState__card"
description={
<FormattedMessage
defaultMessage="Load a data set and a Kibana dashboard."
id="indexPatternManagement.createDataView.emptyState.sampleDataCardDescription"
values={Object {}}
/>
}
icon={
<EuiIcon
color="subdued"
size="xl"
type="heatmap"
/>
}
onClick={[Function]}
title={
<FormattedMessage
defaultMessage="Add sample data"
id="indexPatternManagement.createDataView.emptyState.sampleDataCardTitle"
values={Object {}}
/>
}
/>
</EuiFlexItem>
</EuiFlexGrid>
<EuiSpacer
size="xxl"
/>
<div
className="inpEmptyState__footer"
>
<EuiFlexGroup
justifyContent="center"
> >
<EuiFlexItem <EuiDescriptionList
className="inpEmptyState__footerFlexItem" listItems={
grow={false} Array [
> Object {
<EuiDescriptionList "description": <EuiLink
listItems={ external={true}
Array [ href="http://elastic.co"
Object { target="_blank"
"description": <EuiLink >
external={true} <FormattedMessage
href="http://elastic.co" defaultMessage="Read documentation"
target="_blank" id="indexPatternManagement.createDataView.emptyState.readDocs"
>
<FormattedMessage
defaultMessage="Read documentation"
id="indexPatternManagement.createDataView.emptyState.readDocs"
values={Object {}}
/>
</EuiLink>,
"title": <FormattedMessage
defaultMessage="Want to learn more?"
id="indexPatternManagement.createDataView.emptyState.learnMore"
values={Object {}} values={Object {}}
/>, />
}, </EuiLink>,
] "title": <FormattedMessage
} defaultMessage="Want to learn more?"
/> id="indexPatternManagement.createDataView.emptyState.learnMore"
</EuiFlexItem>
<EuiFlexItem
className="inpEmptyState__footerFlexItem"
grow={false}
>
<EuiDescriptionList
listItems={
Array [
Object {
"description": <EuiLink
data-test-subj="refreshIndicesButton"
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Check for new data"
id="indexPatternManagement.createDataView.emptyState.checkDataButton"
values={Object {}}
/>
<EuiIcon
size="s"
type="refresh"
/>
</EuiLink>,
"title": <FormattedMessage
defaultMessage="Think you already have data?"
id="indexPatternManagement.createDataView.emptyState.haveData"
values={Object {}}
/>,
},
]
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiText
color="subdued"
size="xs"
textAlign="center"
>
<FormattedMessage
defaultMessage="You can also {link}"
id="indexPatternManagement.createDataView.emptyState.createAnywayTxt"
values={
Object {
"link": <EuiLink
data-test-subj="createAnyway"
onClick={[Function]}
>
<FormattedMessage
defaultMessage="create a data view against hidden, system or default indices."
id="indexPatternManagement.createDataView.emptyState.createAnywayLink"
values={Object {}} values={Object {}}
/> />,
</EuiLink>, },
} ]
} }
/> />
</EuiText> </EuiFlexItem>
</div> <EuiFlexItem
</EuiPageContentBody_Deprecated> className="inpEmptyState__footerFlexItem"
</EuiPageContent_Deprecated> grow={false}
</Fragment> >
<EuiDescriptionList
listItems={
Array [
Object {
"description": <EuiLink
data-test-subj="refreshIndicesButton"
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Check for new data"
id="indexPatternManagement.createDataView.emptyState.checkDataButton"
values={Object {}}
/>
<EuiIcon
size="s"
type="refresh"
/>
</EuiLink>,
"title": <FormattedMessage
defaultMessage="Think you already have data?"
id="indexPatternManagement.createDataView.emptyState.haveData"
values={Object {}}
/>,
},
]
}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiText
color="subdued"
size="xs"
textAlign="center"
>
<FormattedMessage
defaultMessage="You can also {link}"
id="indexPatternManagement.createDataView.emptyState.createAnywayTxt"
values={
Object {
"link": <EuiLink
data-test-subj="createAnyway"
onClick={[Function]}
>
<FormattedMessage
defaultMessage="create a data view against hidden, system or default indices."
id="indexPatternManagement.createDataView.emptyState.createAnywayLink"
values={Object {}}
/>
</EuiLink>,
}
}
/>
</EuiText>
</div>
</div>
</EuiPanel>
`; `;

View file

@ -8,13 +8,12 @@
import './empty_index_list_prompt.scss'; import './empty_index_list_prompt.scss';
import React from 'react'; import React from 'react';
import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { import {
EuiPageContentHeader_Deprecated as EuiPageContentHeader, EuiPanel,
EuiPageContentHeaderSection_Deprecated as EuiPageContentHeaderSection, EuiPageHeader,
EuiTitle, EuiTitle,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiPageContent_Deprecated as EuiPageContent,
EuiIcon, EuiIcon,
EuiSpacer, EuiSpacer,
EuiFlexItem, EuiFlexItem,
@ -25,7 +24,6 @@ import {
EuiText, EuiText,
EuiFlexGroup, EuiFlexGroup,
} from '@elastic/eui'; } from '@elastic/eui';
import { ApplicationStart } from '@kbn/core/public'; import { ApplicationStart } from '@kbn/core/public';
export const EmptyIndexListPrompt = ({ export const EmptyIndexListPrompt = ({
@ -61,146 +59,144 @@ export const EmptyIndexListPrompt = ({
); );
return ( return (
<> <EuiPanel
<EuiPageContent className="inpEmptyState"
className="inpEmptyState" data-test-subj="indexPatternEmptyState"
grow={false} color="subdued"
data-test-subj="indexPatternEmptyState" hasShadow={false}
verticalPosition="center" paddingSize="xl"
horizontalPosition="center" css={css`
color="subdued" margin: auto;
> `}
<EuiPageContentHeader> >
<EuiPageContentHeaderSection> <EuiPageHeader>
<EuiTitle> <EuiTitle>
<h2> <h2>
<FormattedMessage
id="indexPatternManagement.createDataView.emptyState.noDataTitle"
defaultMessage="Ready to try Kibana? First, you need data."
/>
</h2>
</EuiTitle>
</EuiPageHeader>
<EuiSpacer size="xl" />
<div>
<EuiFlexGrid className="inpEmptyState__cardGrid" columns={3} responsive={true}>
<EuiFlexItem>
<EuiCard
className="inpEmptyState__card"
onClick={() => {
navigateToApp('integrations', { path: '/browse' });
}}
icon={<EuiIcon size="xl" type="database" color="subdued" />}
title={
<FormattedMessage <FormattedMessage
id="indexPatternManagement.createDataView.emptyState.noDataTitle" id="indexPatternManagement.createDataView.emptyState.integrationCardTitle"
defaultMessage="Ready to try Kibana? First, you need data." defaultMessage="Add integration"
/> />
</h2> }
</EuiTitle> description={
</EuiPageContentHeaderSection> <FormattedMessage
</EuiPageContentHeader> id="indexPatternManagement.createDataView.emptyState.integrationCardDescription"
<EuiSpacer size="m" /> defaultMessage="Add data from a variety of sources."
<EuiPageContentBody> />
<EuiFlexGrid className="inpEmptyState__cardGrid" columns={3} responsive={true}> }
<EuiFlexItem> />
<EuiCard </EuiFlexItem>
className="inpEmptyState__card" <EuiFlexItem>
onClick={() => { <EuiCard
navigateToApp('integrations', { path: '/browse' }); onClick={() => navigateToApp('home', { path: '#/tutorial_directory/fileDataViz' })}
}} className="inpEmptyState__card"
icon={<EuiIcon size="xl" type="database" color="subdued" />} icon={<EuiIcon size="xl" type="document" color="subdued" />}
title={ title={
<FormattedMessage <FormattedMessage
id="indexPatternManagement.createDataView.emptyState.integrationCardTitle" id="indexPatternManagement.createDataView.emptyState.uploadCardTitle"
defaultMessage="Add integration" defaultMessage="Upload a file"
/> />
} }
description={ description={
<FormattedMessage <FormattedMessage
id="indexPatternManagement.createDataView.emptyState.integrationCardDescription" id="indexPatternManagement.createDataView.emptyState.uploadCardDescription"
defaultMessage="Add data from a variety of sources." defaultMessage="Import a CSV, NDJSON, or log file."
/> />
} }
/> />
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem> <EuiFlexItem>
<EuiCard <EuiCard
onClick={() => navigateToApp('home', { path: '#/tutorial_directory/fileDataViz' })} className="inpEmptyState__card"
className="inpEmptyState__card" onClick={() => {
icon={<EuiIcon size="xl" type="document" color="subdued" />} navigateToApp('home', { path: '#/tutorial_directory/sampleData' });
title={ }}
<FormattedMessage icon={<EuiIcon size="xl" type="heatmap" color="subdued" />}
id="indexPatternManagement.createDataView.emptyState.uploadCardTitle" title={
defaultMessage="Upload a file" <FormattedMessage
/> id="indexPatternManagement.createDataView.emptyState.sampleDataCardTitle"
} defaultMessage="Add sample data"
description={ />
<FormattedMessage }
id="indexPatternManagement.createDataView.emptyState.uploadCardDescription" description={
defaultMessage="Import a CSV, NDJSON, or log file." <FormattedMessage
/> id="indexPatternManagement.createDataView.emptyState.sampleDataCardDescription"
} defaultMessage="Load a data set and a Kibana dashboard."
/> />
</EuiFlexItem> }
<EuiFlexItem> />
<EuiCard </EuiFlexItem>
className="inpEmptyState__card" </EuiFlexGrid>
onClick={() => { <EuiSpacer size="xxl" />
navigateToApp('home', { path: '#/tutorial_directory/sampleData' }); <div className="inpEmptyState__footer">
}} <EuiFlexGroup justifyContent="center">
icon={<EuiIcon size="xl" type="heatmap" color="subdued" />} <EuiFlexItem grow={false} className="inpEmptyState__footerFlexItem">
title={ <EuiDescriptionList
<FormattedMessage listItems={[
id="indexPatternManagement.createDataView.emptyState.sampleDataCardTitle" {
defaultMessage="Add sample data" title: (
/> <FormattedMessage
} id="indexPatternManagement.createDataView.emptyState.learnMore"
description={ defaultMessage="Want to learn more?"
<FormattedMessage />
id="indexPatternManagement.createDataView.emptyState.sampleDataCardDescription" ),
defaultMessage="Load a data set and a Kibana dashboard." description: (
/> <EuiLink href={addDataUrl} target="_blank" external>
}
/>
</EuiFlexItem>
</EuiFlexGrid>
<EuiSpacer size="xxl" />
<div className="inpEmptyState__footer">
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false} className="inpEmptyState__footerFlexItem">
<EuiDescriptionList
listItems={[
{
title: (
<FormattedMessage <FormattedMessage
id="indexPatternManagement.createDataView.emptyState.learnMore" id="indexPatternManagement.createDataView.emptyState.readDocs"
defaultMessage="Want to learn more?" defaultMessage="Read documentation"
/> />
), </EuiLink>
description: ( ),
<EuiLink href={addDataUrl} target="_blank" external> },
<FormattedMessage ]}
id="indexPatternManagement.createDataView.emptyState.readDocs" />
defaultMessage="Read documentation" </EuiFlexItem>
/> <EuiFlexItem grow={false} className="inpEmptyState__footerFlexItem">
</EuiLink> <EuiDescriptionList
), listItems={[
}, {
]} title: (
/> <FormattedMessage
</EuiFlexItem> id="indexPatternManagement.createDataView.emptyState.haveData"
<EuiFlexItem grow={false} className="inpEmptyState__footerFlexItem"> defaultMessage="Think you already have data?"
<EuiDescriptionList />
listItems={[ ),
{ description: (
title: ( <EuiLink onClick={onRefresh} data-test-subj="refreshIndicesButton">
<FormattedMessage <FormattedMessage
id="indexPatternManagement.createDataView.emptyState.haveData" id="indexPatternManagement.createDataView.emptyState.checkDataButton"
defaultMessage="Think you already have data?" defaultMessage="Check for new data"
/> />{' '}
), <EuiIcon type="refresh" size="s" />
description: ( </EuiLink>
<EuiLink onClick={onRefresh} data-test-subj="refreshIndicesButton"> ),
<FormattedMessage },
id="indexPatternManagement.createDataView.emptyState.checkDataButton" ]}
defaultMessage="Check for new data" />
/>{' '} </EuiFlexItem>
<EuiIcon type="refresh" size="s" /> </EuiFlexGroup>
</EuiLink> <EuiSpacer />
), {canSaveIndexPattern && createAnywayLink}
}, </div>
]} </div>
/> </EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
{canSaveIndexPattern && createAnywayLink}
</div>
</EuiPageContentBody>
</EuiPageContent>
</>
); );
}; };