mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Fix bugs with Create Index Pattern wizard loading state and createReasonableWait UX helper (#16895)
* Fix bugs with Create Index Pattern wizard loading state and createReasonableWait UX helper. * Fix snapshots and refactor tests for clarity. * Rename createReasonableWait to ensureMinimumTime, refactor for clarity, and support a time argument. * Update form error copy.
This commit is contained in:
parent
0caa5a8acf
commit
8cef9135eb
22 changed files with 397 additions and 747 deletions
|
@ -1,6 +1,16 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CreateIndexPatternWizard should render step 1 1`] = `
|
||||
exports[`CreateIndexPatternWizard defaults to the loading state 1`] = `
|
||||
<div>
|
||||
<Header
|
||||
isIncludingSystemIndices={false}
|
||||
onChangeIncludingSystemIndices={[Function]}
|
||||
/>
|
||||
<LoadingState />
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateIndexPatternWizard renders index pattern step when there are indices 1`] = `
|
||||
<div>
|
||||
<Header
|
||||
isIncludingSystemIndices={false}
|
||||
|
@ -9,9 +19,7 @@ exports[`CreateIndexPatternWizard should render step 1 1`] = `
|
|||
<StepIndexPattern
|
||||
allIndices={
|
||||
Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {},
|
||||
]
|
||||
}
|
||||
esService={Object {}}
|
||||
|
@ -23,7 +31,20 @@ exports[`CreateIndexPatternWizard should render step 1 1`] = `
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateIndexPatternWizard should render step 2 1`] = `
|
||||
exports[`CreateIndexPatternWizard renders the empty state when there are no indices 1`] = `
|
||||
<div>
|
||||
<Header
|
||||
isIncludingSystemIndices={false}
|
||||
onChangeIncludingSystemIndices={[Function]}
|
||||
/>
|
||||
<EmptyState
|
||||
loadingDataDocUrl=""
|
||||
onRefresh={[Function]}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateIndexPatternWizard renders time field step when step is set to 2 1`] = `
|
||||
<div>
|
||||
<Header
|
||||
isIncludingSystemIndices={false}
|
||||
|
@ -37,33 +58,3 @@ exports[`CreateIndexPatternWizard should render step 2 1`] = `
|
|||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateIndexPatternWizard should render the empty state 1`] = `
|
||||
<div>
|
||||
<Header
|
||||
isIncludingSystemIndices={false}
|
||||
onChangeIncludingSystemIndices={[Function]}
|
||||
/>
|
||||
<EmptyState
|
||||
loadingDataDocUrl=""
|
||||
/>
|
||||
<StepIndexPattern
|
||||
allIndices={Array []}
|
||||
esService={Object {}}
|
||||
goToNextStep={[Function]}
|
||||
initialQuery=""
|
||||
isIncludingSystemIndices={false}
|
||||
savedObjectsClient={Object {}}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateIndexPatternWizard should render the loading state 1`] = `
|
||||
<div>
|
||||
<Header
|
||||
isIncludingSystemIndices={false}
|
||||
onChangeIncludingSystemIndices={[Function]}
|
||||
/>
|
||||
<LoadingState />
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -28,43 +28,7 @@ const services = {
|
|||
};
|
||||
|
||||
describe('CreateIndexPatternWizard', () => {
|
||||
it('should render step 1', async () => {
|
||||
const component = shallow(
|
||||
<CreateIndexPatternWizard
|
||||
loadingDataDocUrl={loadingDataDocUrl}
|
||||
initialQuery={initialQuery}
|
||||
services={services}
|
||||
/>
|
||||
);
|
||||
|
||||
// Allow the componentWillMount code to execute
|
||||
// https://github.com/airbnb/enzyme/issues/450
|
||||
await component.update(); // Fire `componentWillMount()`
|
||||
await component.update(); // Force update the component post async actions
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render step 2', async () => {
|
||||
const component = shallow(
|
||||
<CreateIndexPatternWizard
|
||||
loadingDataDocUrl={loadingDataDocUrl}
|
||||
initialQuery={initialQuery}
|
||||
services={services}
|
||||
/>
|
||||
);
|
||||
|
||||
// Allow the componentWillMount code to execute
|
||||
// https://github.com/airbnb/enzyme/issues/450
|
||||
await component.update(); // Fire `componentWillMount()`
|
||||
await component.update(); // Force update the component post async actions
|
||||
|
||||
component.setState({ step: 2 });
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the loading state', async () => {
|
||||
it(`defaults to the loading state`, async () => {
|
||||
const component = shallow(
|
||||
<CreateIndexPatternWizard
|
||||
loadingDataDocUrl={loadingDataDocUrl}
|
||||
|
@ -76,7 +40,7 @@ describe('CreateIndexPatternWizard', () => {
|
|||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the empty state', async () => {
|
||||
it('renders the empty state when there are no indices', async () => {
|
||||
const component = shallow(
|
||||
<CreateIndexPatternWizard
|
||||
loadingDataDocUrl={loadingDataDocUrl}
|
||||
|
@ -85,18 +49,53 @@ describe('CreateIndexPatternWizard', () => {
|
|||
/>
|
||||
);
|
||||
|
||||
// Allow the componentWillMount code to execute
|
||||
// https://github.com/airbnb/enzyme/issues/450
|
||||
await component.update(); // Fire `componentWillMount()`
|
||||
await component.update(); // Force update the component post async actions
|
||||
|
||||
// Remove all indices
|
||||
component.setState({ allIndices: [] });
|
||||
component.setState({
|
||||
isInitiallyLoadingIndices: false,
|
||||
allIndices: [],
|
||||
});
|
||||
|
||||
await component.update();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should create an index pattern', async () => {
|
||||
it('renders index pattern step when there are indices', async () => {
|
||||
const component = shallow(
|
||||
<CreateIndexPatternWizard
|
||||
loadingDataDocUrl={loadingDataDocUrl}
|
||||
initialQuery={initialQuery}
|
||||
services={services}
|
||||
/>
|
||||
);
|
||||
|
||||
component.setState({
|
||||
isInitiallyLoadingIndices: false,
|
||||
allIndices: [{}],
|
||||
});
|
||||
|
||||
await component.update();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders time field step when step is set to 2', async () => {
|
||||
const component = shallow(
|
||||
<CreateIndexPatternWizard
|
||||
loadingDataDocUrl={loadingDataDocUrl}
|
||||
initialQuery={initialQuery}
|
||||
services={services}
|
||||
/>
|
||||
);
|
||||
|
||||
component.setState({
|
||||
isInitiallyLoadingIndices: false,
|
||||
allIndices: [{}],
|
||||
step: 2,
|
||||
});
|
||||
|
||||
await component.update();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('invokes the provided services when creating an index pattern', async () => {
|
||||
const get = jest.fn();
|
||||
const set = jest.fn();
|
||||
const create = jest.fn().mockImplementation(() => 'id');
|
||||
|
|
|
@ -55,7 +55,7 @@ exports[`EmptyState should render normally 1`] = `
|
|||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="xs"
|
||||
size="m"
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
|
@ -71,9 +71,11 @@ exports[`EmptyState should render normally 1`] = `
|
|||
>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
data-test-subj="refreshIndicesButton"
|
||||
fill={false}
|
||||
iconSide="left"
|
||||
iconType="faceHappy"
|
||||
iconType="refresh"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Check for new data
|
||||
|
|
|
@ -1,15 +1,36 @@
|
|||
import React from 'react';
|
||||
import { EmptyState } from '../empty_state';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
describe('EmptyState', () => {
|
||||
it('should render normally', () => {
|
||||
const component = shallow(
|
||||
<EmptyState
|
||||
loadingDataDocUrl="http://www.elastic.co"
|
||||
onRefresh={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('props', () => {
|
||||
describe('onRefresh', () => {
|
||||
it('is called when refresh button is clicked', () => {
|
||||
const onRefreshHandler = sinon.stub();
|
||||
|
||||
const component = shallow(
|
||||
<EmptyState
|
||||
loadingDataDocUrl="http://www.elastic.co"
|
||||
onRefresh={onRefreshHandler}
|
||||
/>
|
||||
);
|
||||
|
||||
component.find('[data-test-subj="refreshIndicesButton"]').simulate('click');
|
||||
|
||||
sinon.assert.calledOnce(onRefreshHandler);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
EuiPanel,
|
||||
|
@ -14,6 +15,7 @@ import {
|
|||
|
||||
export const EmptyState = ({
|
||||
loadingDataDocUrl,
|
||||
onRefresh,
|
||||
}) => (
|
||||
<EuiPanel paddingSize="l">
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center">
|
||||
|
@ -38,10 +40,16 @@ export const EmptyState = ({
|
|||
</EuiLink>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="xs"/>
|
||||
|
||||
<EuiSpacer size="m"/>
|
||||
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton iconType="faceHappy">
|
||||
<EuiButton
|
||||
iconType="refresh"
|
||||
onClick={onRefresh}
|
||||
data-test-subj="refreshIndicesButton"
|
||||
>
|
||||
Check for new data
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
@ -50,3 +58,8 @@ export const EmptyState = ({
|
|||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
);
|
||||
|
||||
EmptyState.propTypes = {
|
||||
loadingDataDocUrl: PropTypes.string.isRequired,
|
||||
onRefresh: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
|
@ -36,27 +36,34 @@ exports[`LoadingState should render normally 1`] = `
|
|||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
size="s"
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
component="div"
|
||||
gutterSize="s"
|
||||
justifyContent="center"
|
||||
responsive={true}
|
||||
wrap={false}
|
||||
>
|
||||
<p
|
||||
style={
|
||||
Object {
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiIcon
|
||||
size="m"
|
||||
type="faceSad"
|
||||
<EuiLoadingSpinner
|
||||
size="l"
|
||||
/>
|
||||
<EuiTextColor
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiText
|
||||
color="subdued"
|
||||
size="s"
|
||||
>
|
||||
Reticulating splines...
|
||||
</EuiTextColor>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
EuiTextColor,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiIcon,
|
||||
EuiText,
|
||||
EuiTextColor,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
export const LoadingState = ({
|
||||
|
||||
}) => (
|
||||
export const LoadingState = () => (
|
||||
<EuiPanel paddingSize="l">
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -22,15 +20,20 @@ export const LoadingState = ({
|
|||
<h2 style={{ textAlign: 'center' }}>Checking for Elasticsearch data</h2>
|
||||
</EuiTextColor>
|
||||
</EuiTitle>
|
||||
|
||||
<EuiSpacer size="s"/>
|
||||
<EuiText size="s">
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
<EuiIcon type="faceSad"/>
|
||||
<EuiTextColor color="subdued">
|
||||
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="l"/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
Reticulating splines...
|
||||
</EuiTextColor>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -1,386 +1,52 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StepIndexPattern should disable the next step if the index pattern exists 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={false}
|
||||
onQueryChanged={[Function]}
|
||||
query="k*"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<StatusMessage
|
||||
matchedIndices={
|
||||
exports[`StepIndexPattern renders errors when input is invalid 1`] = `
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
data-test-subj="createIndexPatternStep1Header"
|
||||
errors={
|
||||
Array [
|
||||
"An index pattern cannot contain spaces or the characters: \\\\, /, ?, \\", <, >, |",
|
||||
]
|
||||
}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={true}
|
||||
isNextStepDisabled={true}
|
||||
onQueryChanged={[Function]}
|
||||
query="?"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern renders indices which match the initial query 1`] = `
|
||||
<IndicesList
|
||||
data-test-subj="createIndexPatternStep1IndicesList"
|
||||
indices={
|
||||
Array [
|
||||
Object {
|
||||
"allIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
],
|
||||
"exactMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"partialMatchedIndices": Array [],
|
||||
"visibleIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
query="k*"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<IndicesList
|
||||
indices={
|
||||
Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
]
|
||||
}
|
||||
query="k*"
|
||||
/>
|
||||
</EuiPanel>
|
||||
"name": "kibana",
|
||||
},
|
||||
]
|
||||
}
|
||||
query="kibana"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should ensure we properly append a wildcard 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={true}
|
||||
onQueryChanged={[Function]}
|
||||
query="k*"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LoadingIndices />
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
</EuiPanel>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should properly fetch indices for the initial query 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={false}
|
||||
onQueryChanged={[Function]}
|
||||
query="k*"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<StatusMessage
|
||||
matchedIndices={
|
||||
exports[`StepIndexPattern renders matching indices when input is valid 1`] = `
|
||||
<IndicesList
|
||||
data-test-subj="createIndexPatternStep1IndicesList"
|
||||
indices={
|
||||
Array [
|
||||
Object {
|
||||
"allIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
],
|
||||
"exactMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"partialMatchedIndices": Array [],
|
||||
"visibleIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
query="k*"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<IndicesList
|
||||
indices={
|
||||
Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
]
|
||||
}
|
||||
query="k*"
|
||||
/>
|
||||
</EuiPanel>
|
||||
"name": "kibana",
|
||||
},
|
||||
]
|
||||
}
|
||||
query="k*"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should render normally 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={true}
|
||||
onQueryChanged={[Function]}
|
||||
query=""
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<StatusMessage
|
||||
matchedIndices={
|
||||
Object {
|
||||
"allIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
],
|
||||
"exactMatchedIndices": Array [],
|
||||
"partialMatchedIndices": Array [],
|
||||
"visibleIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
query=""
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<IndicesList
|
||||
indices={
|
||||
Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
]
|
||||
}
|
||||
query=""
|
||||
/>
|
||||
</EuiPanel>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should render some indices 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={false}
|
||||
onQueryChanged={[Function]}
|
||||
query="k*"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<StatusMessage
|
||||
matchedIndices={
|
||||
Object {
|
||||
"allIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
],
|
||||
"exactMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"partialMatchedIndices": Array [],
|
||||
"visibleIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
query="k*"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<IndicesList
|
||||
indices={
|
||||
Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
]
|
||||
}
|
||||
query="k*"
|
||||
/>
|
||||
</EuiPanel>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should render the loading state 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={true}
|
||||
onQueryChanged={[Function]}
|
||||
query="k"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LoadingIndices />
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
</EuiPanel>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should search for partial indices for queries not ending in a wildcard 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={Array []}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={false}
|
||||
isNextStepDisabled={false}
|
||||
onQueryChanged={[Function]}
|
||||
query="k"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<StatusMessage
|
||||
matchedIndices={
|
||||
Object {
|
||||
"allIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
Object {
|
||||
"name": "es",
|
||||
},
|
||||
],
|
||||
"exactMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"partialMatchedIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
"visibleIndices": Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
query="k"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<IndicesList
|
||||
indices={
|
||||
Array [
|
||||
Object {
|
||||
"name": "kibana",
|
||||
},
|
||||
]
|
||||
}
|
||||
query="k"
|
||||
/>
|
||||
</EuiPanel>
|
||||
`;
|
||||
|
||||
exports[`StepIndexPattern should show errors 1`] = `
|
||||
<EuiPanel
|
||||
grow={true}
|
||||
hasShadow={false}
|
||||
paddingSize="l"
|
||||
>
|
||||
<Header
|
||||
characterList="\\\\, /, ?, \\", <, >, |"
|
||||
errors={
|
||||
Array [
|
||||
"Your input contains invalid characters or spaces. Please omit: \\\\, /, ?, \\", <, >, |",
|
||||
]
|
||||
}
|
||||
goToNextStep={[Function]}
|
||||
isInputInvalid={true}
|
||||
isNextStepDisabled={true}
|
||||
onQueryChanged={[Function]}
|
||||
query="?"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<LoadingIndices />
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
</EuiPanel>
|
||||
exports[`StepIndexPattern renders the loading state 1`] = `
|
||||
<LoadingIndices
|
||||
data-test-subj="createIndexPatternStep1Loading"
|
||||
/>
|
||||
`;
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { StepIndexPattern } from '../step_index_pattern';
|
||||
|
||||
jest.mock('../components/indices_list', () => ({ IndicesList: 'IndicesList' }));
|
||||
jest.mock('../components/loading_indices', () => ({ LoadingIndices: 'LoadingIndices' }));
|
||||
jest.mock('../components/status_message', () => ({ StatusMessage: 'StatusMessage' }));
|
||||
jest.mock('../components/header', () => ({ Header: 'Header' }));
|
||||
jest.mock('../../../lib/create_reasonable_wait', () => ({ createReasonableWait: fn => fn() }));
|
||||
jest.mock('../../../lib/ensure_minimum_time', () => ({
|
||||
ensureMinimumTime: async (promises) => Array.isArray(promises) ? await Promise.all(promises) : await promises
|
||||
}));
|
||||
jest.mock('../../../lib/get_indices', () => ({
|
||||
getIndices: () => {
|
||||
return [
|
||||
|
@ -23,166 +20,73 @@ const savedObjectsClient = {
|
|||
};
|
||||
const goToNextStep = () => {};
|
||||
|
||||
const createComponent = props => {
|
||||
return shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={savedObjectsClient}
|
||||
goToNextStep={goToNextStep}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
describe('StepIndexPattern', () => {
|
||||
it('should render normally', () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={savedObjectsClient}
|
||||
goToNextStep={goToNextStep}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
it('renders the loading state', () => {
|
||||
const component = createComponent();
|
||||
component.setState({ isLoadingIndices: true });
|
||||
expect(component.find('[data-test-subj="createIndexPatternStep1Loading"]')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the loading state', () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={savedObjectsClient}
|
||||
goToNextStep={goToNextStep}
|
||||
/>
|
||||
);
|
||||
|
||||
component.setState({ query: 'k', isLoadingIndices: true });
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render some indices', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={savedObjectsClient}
|
||||
goToNextStep={goToNextStep}
|
||||
/>
|
||||
);
|
||||
|
||||
const instance = component.instance();
|
||||
|
||||
await instance.onQueryChanged({
|
||||
target: { value: 'k' }
|
||||
});
|
||||
it('renders indices which match the initial query', async () => {
|
||||
const component = createComponent({ initialQuery: 'kibana' });
|
||||
|
||||
// Ensure all promises resolve
|
||||
await new Promise(resolve => process.nextTick(resolve));
|
||||
// Ensure the state changes are reflected
|
||||
await component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(component.find('[data-test-subj="createIndexPatternStep1IndicesList"]')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should show errors', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={savedObjectsClient}
|
||||
goToNextStep={goToNextStep}
|
||||
/>
|
||||
);
|
||||
|
||||
it('renders errors when input is invalid', async () => {
|
||||
const component = createComponent();
|
||||
const instance = component.instance();
|
||||
instance.onQueryChanged({ target: { value: '?' } });
|
||||
|
||||
await instance.onQueryChanged({
|
||||
target: { value: '?' }
|
||||
});
|
||||
|
||||
// Ensure all promises resolve
|
||||
await new Promise(resolve => process.nextTick(resolve));
|
||||
// Ensure the state changes are reflected
|
||||
component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(component.find('[data-test-subj="createIndexPatternStep1Header"]')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should properly fetch indices for the initial query', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={savedObjectsClient}
|
||||
goToNextStep={goToNextStep}
|
||||
initialQuery="k*"
|
||||
/>
|
||||
);
|
||||
|
||||
// Allow the componentWillMount code to execute
|
||||
// https://github.com/airbnb/enzyme/issues/450
|
||||
await component.update(); // Fire `componentWillMount()`
|
||||
await component.update(); // Force update the component post async actions
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should disable the next step if the index pattern exists', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={{
|
||||
find: () => ({ savedObjects: [
|
||||
{ attributes: { title: 'k*' } }
|
||||
] })
|
||||
}}
|
||||
goToNextStep={goToNextStep}
|
||||
initialQuery="k*"
|
||||
/>
|
||||
);
|
||||
|
||||
// Allow the componentWillMount code to execute
|
||||
// https://github.com/airbnb/enzyme/issues/450
|
||||
await component.update(); // Fire `componentWillMount()`
|
||||
await component.update(); // Force update the component post async actions
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should ensure we properly append a wildcard', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={{
|
||||
find: () => ({ savedObjects: [
|
||||
{ attributes: { title: 'k*' } }
|
||||
] })
|
||||
}}
|
||||
goToNextStep={goToNextStep}
|
||||
/>
|
||||
);
|
||||
|
||||
it('renders matching indices when input is valid', async () => {
|
||||
const component = createComponent();
|
||||
const instance = component.instance();
|
||||
|
||||
instance.onQueryChanged({ target: { value: 'k' } });
|
||||
await component.update();
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
// Ensure all promises resolve
|
||||
await new Promise(resolve => process.nextTick(resolve));
|
||||
// Ensure the state changes are reflected
|
||||
component.update();
|
||||
|
||||
expect(component.find('[data-test-subj="createIndexPatternStep1IndicesList"]')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should search for partial indices for queries not ending in a wildcard', async () => {
|
||||
const component = shallow(
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
isIncludingSystemIndices={false}
|
||||
esService={esService}
|
||||
savedObjectsClient={savedObjectsClient}
|
||||
goToNextStep={goToNextStep}
|
||||
initialQuery="k"
|
||||
/>
|
||||
);
|
||||
it('appends a wildcard automatically to queries', async () => {
|
||||
const component = createComponent();
|
||||
const instance = component.instance();
|
||||
instance.onQueryChanged({ target: { value: 'k' } });
|
||||
expect(component.state('query')).toBe('k*');
|
||||
});
|
||||
|
||||
// Allow the componentWillMount code to execute
|
||||
// https://github.com/airbnb/enzyme/issues/450
|
||||
await component.update(); // Fire `componentWillMount()`
|
||||
await component.update(); // Force update the component post async actions
|
||||
await component.update(); // There are two actions so we apparently need to call this again
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
it('disables the next step if the index pattern exists', async () => {
|
||||
const component = createComponent();
|
||||
component.setState({ indexPatternExists: true });
|
||||
expect(component.find('Header').prop('isNextStepDisabled')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,7 +45,7 @@ exports[`Header should mark the input as invalid 1`] = `
|
|||
as a wildcard in your index pattern.
|
||||
</p>
|
||||
<p>
|
||||
You can't use empty spaces or the characters
|
||||
You can't use spaces or the characters
|
||||
<strong>
|
||||
%
|
||||
</strong>
|
||||
|
@ -131,7 +131,7 @@ exports[`Header should render normally 1`] = `
|
|||
as a wildcard in your index pattern.
|
||||
</p>
|
||||
<p>
|
||||
You can't use empty spaces or the characters
|
||||
You can't use spaces or the characters
|
||||
<strong>
|
||||
%
|
||||
</strong>
|
||||
|
|
|
@ -19,8 +19,9 @@ export const Header = ({
|
|||
onQueryChanged,
|
||||
goToNextStep,
|
||||
isNextStepDisabled,
|
||||
...rest
|
||||
}) => (
|
||||
<div>
|
||||
<div {...rest}>
|
||||
<EuiTitle size="s">
|
||||
<h2>
|
||||
Step 1 of 2: Define index pattern
|
||||
|
@ -39,7 +40,7 @@ export const Header = ({
|
|||
helpText={
|
||||
<div>
|
||||
<p>You can use a <strong>*</strong> as a wildcard in your index pattern.</p>
|
||||
<p>You can't use empty spaces or the characters <strong>{characterList}</strong>.</p>
|
||||
<p>You can't use spaces or the characters <strong>{characterList}</strong>.</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
|
@ -149,7 +149,7 @@ export class IndicesList extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { indices, query } = this.props;
|
||||
const { indices, query, ...rest } = this.props;
|
||||
|
||||
const queryWithoutWildcard = query.endsWith('*') ? query.substr(0, query.length - 1) : query;
|
||||
|
||||
|
@ -165,7 +165,7 @@ export class IndicesList extends Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div {...rest}>
|
||||
<EuiTable>
|
||||
<EuiTableBody>
|
||||
{rows}
|
||||
|
|
|
@ -8,17 +8,23 @@ import {
|
|||
EuiLoadingSpinner,
|
||||
} from '@elastic/eui';
|
||||
|
||||
export const LoadingIndices = () => (
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center">
|
||||
export const LoadingIndices = ({ ...rest }) => (
|
||||
<EuiFlexGroup
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
{...rest}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="m" />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<EuiTextColor color="subdued">
|
||||
Looking for matching indices...
|
||||
</EuiTextColor>
|
||||
</EuiText>
|
||||
|
||||
<EuiText size="s" style={{ textAlign: 'center' }}>
|
||||
<EuiTextColor color="subdued">
|
||||
Just a sec...
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
containsInvalidCharacters,
|
||||
getMatchedIndices,
|
||||
canAppendWildcard,
|
||||
createReasonableWait
|
||||
ensureMinimumTime
|
||||
} from '../../lib';
|
||||
import { LoadingIndices } from './components/loading_indices';
|
||||
import { StatusMessage } from './components/status_message';
|
||||
|
@ -74,19 +74,26 @@ export class StepIndexPattern extends Component {
|
|||
}
|
||||
|
||||
this.setState({ isLoadingIndices: true, indexPatternExists: false });
|
||||
|
||||
if (query.endsWith('*')) {
|
||||
const exactMatchedIndices = await getIndices(esService, query, MAX_SEARCH_SIZE);
|
||||
createReasonableWait(() => this.setState({ exactMatchedIndices, isLoadingIndices: false }));
|
||||
}
|
||||
else {
|
||||
const partialMatchedIndices = await getIndices(esService, `${query}*`, MAX_SEARCH_SIZE);
|
||||
const exactMatchedIndices = await getIndices(esService, query, MAX_SEARCH_SIZE);
|
||||
createReasonableWait(() => this.setState({
|
||||
partialMatchedIndices,
|
||||
exactMatchedIndices,
|
||||
isLoadingIndices: false
|
||||
}));
|
||||
const exactMatchedIndices = await ensureMinimumTime(getIndices(esService, query, MAX_SEARCH_SIZE));
|
||||
this.setState({ exactMatchedIndices, isLoadingIndices: false });
|
||||
return;
|
||||
}
|
||||
|
||||
const [
|
||||
partialMatchedIndices,
|
||||
exactMatchedIndices,
|
||||
] = await ensureMinimumTime([
|
||||
getIndices(esService, `${query}*`, MAX_SEARCH_SIZE),
|
||||
getIndices(esService, query, MAX_SEARCH_SIZE),
|
||||
]);
|
||||
|
||||
this.setState({
|
||||
partialMatchedIndices,
|
||||
exactMatchedIndices,
|
||||
isLoadingIndices: false
|
||||
});
|
||||
}
|
||||
|
||||
onQueryChanged = e => {
|
||||
|
@ -98,8 +105,7 @@ export class StepIndexPattern extends Component {
|
|||
query += '*';
|
||||
this.setState({ appendedWildcard: true });
|
||||
setTimeout(() => target.setSelectionRange(1, 1));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (query === '*' && appendedWildcard) {
|
||||
query = '';
|
||||
this.setState({ appendedWildcard: false });
|
||||
|
@ -118,7 +124,7 @@ export class StepIndexPattern extends Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<LoadingIndices/>
|
||||
<LoadingIndices data-test-subj="createIndexPatternStep1Loading" />
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -151,6 +157,7 @@ export class StepIndexPattern extends Component {
|
|||
|
||||
return (
|
||||
<IndicesList
|
||||
data-test-subj="createIndexPatternStep1IndicesList"
|
||||
query={query}
|
||||
indices={indicesToList}
|
||||
/>
|
||||
|
@ -188,7 +195,7 @@ export class StepIndexPattern extends Component {
|
|||
containsErrors = true;
|
||||
}
|
||||
else if (!containsInvalidCharacters(query, ILLEGAL_CHARACTERS)) {
|
||||
errors.push(`Your input contains invalid characters or spaces. Please omit: ${characterList}`);
|
||||
errors.push(`An index pattern cannot contain spaces or the characters: ${characterList}`);
|
||||
containsErrors = true;
|
||||
}
|
||||
|
||||
|
@ -197,6 +204,7 @@ export class StepIndexPattern extends Component {
|
|||
|
||||
return (
|
||||
<Header
|
||||
data-test-subj="createIndexPatternStep1Header"
|
||||
isInputInvalid={isInputInvalid}
|
||||
errors={errors}
|
||||
characterList={characterList}
|
||||
|
|
|
@ -57,12 +57,12 @@ exports[`StepTimeField should render a selected timeField 1`] = `
|
|||
"value": "",
|
||||
},
|
||||
Object {
|
||||
"isDisabled": undefined,
|
||||
"disabled": undefined,
|
||||
"text": "@timestamp",
|
||||
"value": "@timestamp",
|
||||
},
|
||||
Object {
|
||||
"isDisabled": undefined,
|
||||
"disabled": undefined,
|
||||
"text": "name",
|
||||
"value": "name",
|
||||
},
|
||||
|
@ -251,12 +251,12 @@ exports[`StepTimeField should render timeFields 1`] = `
|
|||
"value": "",
|
||||
},
|
||||
Object {
|
||||
"isDisabled": undefined,
|
||||
"disabled": undefined,
|
||||
"text": "@timestamp",
|
||||
"value": "@timestamp",
|
||||
},
|
||||
Object {
|
||||
"isDisabled": undefined,
|
||||
"disabled": undefined,
|
||||
"text": "name",
|
||||
"value": "name",
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { extractTimeFields } from '../../lib/extract_time_fields';
|
||||
import { ensureMinimumTime, extractTimeFields } from '../../lib';
|
||||
|
||||
import { Header } from './components/header';
|
||||
import { TimeField } from './components/time_field';
|
||||
|
@ -16,7 +16,6 @@ import {
|
|||
EuiLoadingSpinner,
|
||||
} from '@elastic/eui';
|
||||
|
||||
|
||||
export class StepTimeField extends Component {
|
||||
static propTypes = {
|
||||
indexPattern: PropTypes.string.isRequired,
|
||||
|
@ -46,7 +45,7 @@ export class StepTimeField extends Component {
|
|||
const { indexPatternsService, indexPattern } = this.props;
|
||||
|
||||
this.setState({ isFetchingTimeFields: true });
|
||||
const fields = await indexPatternsService.fieldsFetcher.fetchForWildcard(indexPattern);
|
||||
const fields = await ensureMinimumTime(indexPatternsService.fieldsFetcher.fetchForWildcard(indexPattern));
|
||||
const timeFields = extractTimeFields(fields);
|
||||
|
||||
this.setState({ timeFields, isFetchingTimeFields: false });
|
||||
|
@ -108,7 +107,7 @@ export class StepTimeField extends Component {
|
|||
...timeFields.map(timeField => ({
|
||||
text: timeField.display,
|
||||
value: timeField.fieldName || '',
|
||||
isDisabled: timeFields.isDisabled,
|
||||
disabled: timeFields.isDisabled,
|
||||
}))
|
||||
]
|
||||
: [];
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { ensureMinimumTime } from './lib';
|
||||
import { StepIndexPattern } from './components/step_index_pattern';
|
||||
import { StepTimeField } from './components/step_time_field';
|
||||
import { Header } from './components/header';
|
||||
|
@ -35,8 +36,13 @@ export class CreateIndexPatternWizard extends Component {
|
|||
}
|
||||
|
||||
async componentWillMount() {
|
||||
this.fetchIndices();
|
||||
}
|
||||
|
||||
fetchIndices = async () => {
|
||||
this.setState({ allIndices: [], isInitiallyLoadingIndices: true });
|
||||
const { services } = this.props;
|
||||
const allIndices = await getIndices(services.es, `*`, MAX_SEARCH_SIZE);
|
||||
const allIndices = await ensureMinimumTime(getIndices(services.es, `*`, MAX_SEARCH_SIZE));
|
||||
this.setState({ allIndices, isInitiallyLoadingIndices: false });
|
||||
}
|
||||
|
||||
|
@ -87,32 +93,7 @@ export class CreateIndexPatternWizard extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderInitialLoadingState() {
|
||||
const { isInitiallyLoadingIndices } = this.state;
|
||||
|
||||
if (!isInitiallyLoadingIndices) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<LoadingState/>
|
||||
);
|
||||
}
|
||||
|
||||
renderInitialEmptyState() {
|
||||
const { allIndices, isInitiallyLoadingIndices } = this.state;
|
||||
const { loadingDataDocUrl } = this.props;
|
||||
|
||||
if (allIndices.length > 0 || isInitiallyLoadingIndices) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EmptyState loadingDataDocUrl={loadingDataDocUrl}/>
|
||||
);
|
||||
}
|
||||
|
||||
renderStepOne() {
|
||||
renderContent() {
|
||||
const {
|
||||
allIndices,
|
||||
isInitiallyLoadingIndices,
|
||||
|
@ -121,50 +102,52 @@ export class CreateIndexPatternWizard extends Component {
|
|||
indexPattern,
|
||||
} = this.state;
|
||||
|
||||
if (isInitiallyLoadingIndices || step !== 1) {
|
||||
return null;
|
||||
if (isInitiallyLoadingIndices) {
|
||||
return <LoadingState />;
|
||||
}
|
||||
|
||||
const { services, initialQuery } = this.props;
|
||||
|
||||
return (
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
initialQuery={indexPattern || initialQuery}
|
||||
isIncludingSystemIndices={isIncludingSystemIndices}
|
||||
esService={services.es}
|
||||
savedObjectsClient={services.savedObjectsClient}
|
||||
goToNextStep={this.goToTimeFieldStep}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderStepTwo() {
|
||||
const { step, indexPattern } = this.state;
|
||||
const { services } = this.props;
|
||||
|
||||
if (step !== 2) {
|
||||
return null;
|
||||
if (allIndices.length === 0) {
|
||||
const { loadingDataDocUrl } = this.props;
|
||||
return <EmptyState loadingDataDocUrl={loadingDataDocUrl} onRefresh={this.fetchIndices} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<StepTimeField
|
||||
indexPattern={indexPattern}
|
||||
indexPatternsService={services.indexPatterns}
|
||||
goToPreviousStep={this.goToIndexPatternStep}
|
||||
createIndexPattern={this.createIndexPattern}
|
||||
/>
|
||||
);
|
||||
if (step === 1) {
|
||||
const { services, initialQuery } = this.props;
|
||||
return (
|
||||
<StepIndexPattern
|
||||
allIndices={allIndices}
|
||||
initialQuery={indexPattern || initialQuery}
|
||||
isIncludingSystemIndices={isIncludingSystemIndices}
|
||||
esService={services.es}
|
||||
savedObjectsClient={services.savedObjectsClient}
|
||||
goToNextStep={this.goToTimeFieldStep}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (step === 2) {
|
||||
const { services } = this.props;
|
||||
return (
|
||||
<StepTimeField
|
||||
indexPattern={indexPattern}
|
||||
indexPatternsService={services.indexPatterns}
|
||||
goToPreviousStep={this.goToIndexPatternStep}
|
||||
createIndexPattern={this.createIndexPattern}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const header = this.renderHeader();
|
||||
const content = this.renderContent();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.renderHeader()}
|
||||
{this.renderInitialLoadingState()}
|
||||
{this.renderInitialEmptyState()}
|
||||
{this.renderStepOne()}
|
||||
{this.renderStepTwo()}
|
||||
{header}
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import { createReasonableWait } from '../create_reasonable_wait';
|
||||
import sinon from 'sinon';
|
||||
|
||||
describe('createReasonableWait', () => {
|
||||
it('should eventually calls the callback', () => {
|
||||
const callback = sinon.spy();
|
||||
createReasonableWait(callback);
|
||||
expect(callback.notCalled).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
import { ensureMinimumTime } from '../ensure_minimum_time';
|
||||
|
||||
describe('ensureMinimumTime', () => {
|
||||
it('resolves single promise', async (done) => {
|
||||
const promiseA = new Promise(resolve => resolve('a'));
|
||||
const a = await ensureMinimumTime(promiseA, 0);
|
||||
expect(a).toBe('a');
|
||||
done();
|
||||
});
|
||||
|
||||
it('resolves multiple promises', async (done) => {
|
||||
const promiseA = new Promise(resolve => resolve('a'));
|
||||
const promiseB = new Promise(resolve => resolve('b'));
|
||||
const [ a, b ] = await ensureMinimumTime([promiseA, promiseB], 0);
|
||||
expect(a).toBe('a');
|
||||
expect(b).toBe('b');
|
||||
done();
|
||||
});
|
||||
|
||||
it('resolves in the amount of time provided, at minimum', async (done) => {
|
||||
const startTime = new Date().getTime();
|
||||
const promise = new Promise(resolve => resolve());
|
||||
await ensureMinimumTime(promise, 100);
|
||||
const endTime = new Date().getTime();
|
||||
expect(endTime - startTime).toBeGreaterThanOrEqual(100);
|
||||
done();
|
||||
});
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
export function createReasonableWait(cb) {
|
||||
return setTimeout(cb, 100);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* When you make an async request, typically you want to show the user a spinner while they wait.
|
||||
* However, if the request takes less than 300 ms, the spinner will flicker in the UI and the user
|
||||
* won't have time to register it as a spinner. This function ensures the spinner (or whatever
|
||||
* you're showing the user) displays for at least 300 ms, even if the request completes before then.
|
||||
*/
|
||||
|
||||
export const DEFAULT_MINIMUM_TIME_MS = 300;
|
||||
|
||||
export async function ensureMinimumTime(promiseOrPromises, minimumTimeMs = DEFAULT_MINIMUM_TIME_MS) {
|
||||
let returnValue;
|
||||
|
||||
// Block on the async action and start the clock.
|
||||
const asyncActionStartTime = new Date().getTime();
|
||||
if (Array.isArray(promiseOrPromises)) {
|
||||
returnValue = await Promise.all(promiseOrPromises);
|
||||
} else {
|
||||
returnValue = await promiseOrPromises;
|
||||
}
|
||||
|
||||
// Measure how long the async action took to complete.
|
||||
const asyncActionCompletionTime = new Date().getTime();
|
||||
const asyncActionDuration = asyncActionCompletionTime - asyncActionStartTime;
|
||||
|
||||
// Wait longer if the async action completed too quickly.
|
||||
if (asyncActionDuration < minimumTimeMs) {
|
||||
const additionalWaitingTime = minimumTimeMs - (asyncActionCompletionTime - asyncActionStartTime);
|
||||
await new Promise(resolve => setTimeout(resolve, additionalWaitingTime));
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
export { canAppendWildcard } from './can_append_wildcard';
|
||||
|
||||
export { createReasonableWait } from './create_reasonable_wait';
|
||||
export { ensureMinimumTime } from './ensure_minimum_time';
|
||||
|
||||
export { getIndices } from './get_indices';
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue