mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* handle EuiSearchBar query parse failures * I18n parse failure messages * review updates * more cleanup on settings search.test
This commit is contained in:
parent
95c4cc65c4
commit
e6ff53265a
6 changed files with 225 additions and 90 deletions
|
@ -3,6 +3,11 @@
|
|||
exports[`Table should render normally 1`] = `
|
||||
<React.Fragment>
|
||||
<EuiSearchBar
|
||||
box={
|
||||
Object {
|
||||
"data-test-subj": "savedObjectSearchBar",
|
||||
}
|
||||
}
|
||||
filters={
|
||||
Array [
|
||||
Object {
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { keyCodes } from '@elastic/eui/lib/services';
|
||||
|
||||
jest.mock('ui/errors', () => ({
|
||||
SavedObjectNotFound: class SavedObjectNotFound extends Error {
|
||||
|
@ -39,37 +41,62 @@ jest.mock('ui/chrome', () => ({
|
|||
|
||||
import { Table } from '../table';
|
||||
|
||||
const defaultProps = {
|
||||
selectedSavedObjects: [1],
|
||||
selectionConfig: {
|
||||
onSelectionChange: () => {},
|
||||
},
|
||||
filterOptions: [{ value: 2 }],
|
||||
onDelete: () => {},
|
||||
onExport: () => {},
|
||||
getEditUrl: () => {},
|
||||
goInApp: () => {},
|
||||
pageIndex: 1,
|
||||
pageSize: 2,
|
||||
items: [3],
|
||||
itemId: 'id',
|
||||
totalItemCount: 3,
|
||||
onQueryChange: () => {},
|
||||
onTableChange: () => {},
|
||||
isSearching: false,
|
||||
onShowRelationships: () => {},
|
||||
};
|
||||
|
||||
describe('Table', () => {
|
||||
it('should render normally', () => {
|
||||
const props = {
|
||||
selectedSavedObjects: [1],
|
||||
selectionConfig: {
|
||||
onSelectionChange: () => {},
|
||||
},
|
||||
filterOptions: [{ value: 2 }],
|
||||
onDelete: () => {},
|
||||
onExport: () => {},
|
||||
getEditUrl: () => {},
|
||||
goInApp: () => {},
|
||||
|
||||
pageIndex: 1,
|
||||
pageSize: 2,
|
||||
items: [3],
|
||||
itemId: 'id',
|
||||
totalItemCount: 3,
|
||||
onQueryChange: () => {},
|
||||
onTableChange: () => {},
|
||||
isSearching: false,
|
||||
|
||||
onShowRelationships: () => {},
|
||||
};
|
||||
|
||||
const component = shallowWithIntl(
|
||||
<Table.WrappedComponent
|
||||
{...props}
|
||||
{...defaultProps}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should handle query parse error', () => {
|
||||
const onQueryChangeMock = jest.fn();
|
||||
const customizedProps = {
|
||||
...defaultProps,
|
||||
onQueryChange: onQueryChangeMock
|
||||
};
|
||||
|
||||
const component = mountWithIntl(
|
||||
<Table.WrappedComponent
|
||||
{...customizedProps}
|
||||
/>
|
||||
);
|
||||
const searchBar = findTestSubject(component, 'savedObjectSearchBar');
|
||||
|
||||
// Send invalid query
|
||||
searchBar.simulate('keyup', { keyCode: keyCodes.ENTER, target: { value: '?' } });
|
||||
expect(onQueryChangeMock).toHaveBeenCalledTimes(0);
|
||||
expect(component.state().isSearchTextValid).toBe(false);
|
||||
|
||||
onQueryChangeMock.mockReset();
|
||||
|
||||
// Send valid query to ensure component can recover from invalid query
|
||||
searchBar.simulate('keyup', { keyCode: keyCodes.ENTER, target: { value: 'I am valid' } });
|
||||
expect(onQueryChangeMock).toHaveBeenCalledTimes(1);
|
||||
expect(component.state().isSearchTextValid).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,7 +27,8 @@ import {
|
|||
EuiIcon,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiToolTip
|
||||
EuiToolTip,
|
||||
EuiFormErrorText
|
||||
} from '@elastic/eui';
|
||||
import { getSavedObjectLabel, getSavedObjectIcon } from '../../../../lib';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
|
@ -61,6 +62,27 @@ class TableUI extends PureComponent {
|
|||
onShowRelationships: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
isSearchTextValid: true,
|
||||
parseErrorMessage: null,
|
||||
}
|
||||
|
||||
onChange = ({ query, error }) => {
|
||||
if (error) {
|
||||
this.setState({
|
||||
isSearchTextValid: false,
|
||||
parseErrorMessage: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isSearchTextValid: true,
|
||||
parseErrorMessage: null,
|
||||
});
|
||||
this.props.onQueryChange({ query });
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
pageIndex,
|
||||
|
@ -74,7 +96,6 @@ class TableUI extends PureComponent {
|
|||
onDelete,
|
||||
onExport,
|
||||
selectedSavedObjects,
|
||||
onQueryChange,
|
||||
onTableChange,
|
||||
goInApp,
|
||||
getEditUrl,
|
||||
|
@ -182,11 +203,25 @@ class TableUI extends PureComponent {
|
|||
},
|
||||
];
|
||||
|
||||
let queryParseError;
|
||||
if (!this.state.isSearchTextValid) {
|
||||
const parseErrorMsg = intl.formatMessage({
|
||||
id: 'kbn.management.objects.objectsTable.searchBar.unableToParseQueryErrorMessage',
|
||||
defaultMessage: 'Unable to parse query',
|
||||
});
|
||||
queryParseError = (
|
||||
<EuiFormErrorText>
|
||||
{`${parseErrorMsg}. ${this.state.parseErrorMessage}`}
|
||||
</EuiFormErrorText>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiSearchBar
|
||||
box={{ 'data-test-subj': 'savedObjectSearchBar' }}
|
||||
filters={filters}
|
||||
onChange={onQueryChange}
|
||||
onChange={this.onChange}
|
||||
toolsRight={[
|
||||
<EuiButton
|
||||
key="deleteSO"
|
||||
|
@ -213,6 +248,7 @@ class TableUI extends PureComponent {
|
|||
</EuiButton>,
|
||||
]}
|
||||
/>
|
||||
{queryParseError}
|
||||
<EuiSpacer size="s" />
|
||||
<div data-test-subj="savedObjectsTable">
|
||||
<EuiBasicTable
|
||||
|
|
|
@ -1,58 +1,61 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Search should render normally 1`] = `
|
||||
<EuiSearchBar
|
||||
box={
|
||||
Object {
|
||||
"aria-label": "Search advanced settings",
|
||||
"incremental": true,
|
||||
}
|
||||
}
|
||||
filters={
|
||||
Array [
|
||||
<React.Fragment>
|
||||
<EuiSearchBar
|
||||
box={
|
||||
Object {
|
||||
"field": "category",
|
||||
"multiSelect": "or",
|
||||
"name": "Category",
|
||||
"options": Array [
|
||||
Object {
|
||||
"name": "General",
|
||||
"value": "general",
|
||||
},
|
||||
Object {
|
||||
"name": "Dashboard",
|
||||
"value": "dashboard",
|
||||
},
|
||||
Object {
|
||||
"name": "HiddenCategory",
|
||||
"value": "hiddenCategory",
|
||||
},
|
||||
Object {
|
||||
"name": "X-pack",
|
||||
"value": "x-pack",
|
||||
},
|
||||
],
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
]
|
||||
}
|
||||
onChange={[Function]}
|
||||
query={
|
||||
Query {
|
||||
"ast": _AST {
|
||||
"_clauses": Array [],
|
||||
"_indexedClauses": Object {
|
||||
"field": Object {},
|
||||
"is": Object {},
|
||||
"term": Array [],
|
||||
},
|
||||
},
|
||||
"syntax": Object {
|
||||
"parse": [Function],
|
||||
"print": [Function],
|
||||
},
|
||||
"text": "",
|
||||
"aria-label": "Search advanced settings",
|
||||
"data-test-subj": "settingsSearchBar",
|
||||
"incremental": true,
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
filters={
|
||||
Array [
|
||||
Object {
|
||||
"field": "category",
|
||||
"multiSelect": "or",
|
||||
"name": "Category",
|
||||
"options": Array [
|
||||
Object {
|
||||
"name": "General",
|
||||
"value": "general",
|
||||
},
|
||||
Object {
|
||||
"name": "Dashboard",
|
||||
"value": "dashboard",
|
||||
},
|
||||
Object {
|
||||
"name": "HiddenCategory",
|
||||
"value": "hiddenCategory",
|
||||
},
|
||||
Object {
|
||||
"name": "X-pack",
|
||||
"value": "x-pack",
|
||||
},
|
||||
],
|
||||
"type": "field_value_selection",
|
||||
},
|
||||
]
|
||||
}
|
||||
onChange={[Function]}
|
||||
query={
|
||||
Query {
|
||||
"ast": _AST {
|
||||
"_clauses": Array [],
|
||||
"_indexedClauses": Object {
|
||||
"field": Object {},
|
||||
"is": Object {},
|
||||
"term": Array [],
|
||||
},
|
||||
},
|
||||
"syntax": Object {
|
||||
"parse": [Function],
|
||||
"print": [Function],
|
||||
},
|
||||
"text": "",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
`;
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { Fragment, PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
import {
|
||||
EuiSearchBar,
|
||||
EuiFormErrorText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { getCategoryName } from '../../lib';
|
||||
|
@ -46,11 +47,33 @@ class SearchUI extends PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
state = {
|
||||
isSearchTextValid: true,
|
||||
parseErrorMessage: null,
|
||||
}
|
||||
|
||||
onChange = ({ query, error }) => {
|
||||
if (error) {
|
||||
this.setState({
|
||||
isSearchTextValid: false,
|
||||
parseErrorMessage: error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isSearchTextValid: true,
|
||||
parseErrorMessage: null,
|
||||
});
|
||||
this.props.onQueryChange({ query });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { query, onQueryChange, intl } = this.props;
|
||||
const { query, intl } = this.props;
|
||||
|
||||
const box = {
|
||||
incremental: true,
|
||||
'data-test-subj': 'settingsSearchBar',
|
||||
'aria-label': intl.formatMessage({
|
||||
id: 'kbn.management.settings.searchBarAriaLabel',
|
||||
defaultMessage: 'Search advanced settings',
|
||||
|
@ -71,14 +94,29 @@ class SearchUI extends PureComponent {
|
|||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiSearchBar
|
||||
box={box}
|
||||
filters={filters}
|
||||
onChange={onQueryChange}
|
||||
query={query}
|
||||
/>
|
||||
let queryParseError;
|
||||
if (!this.state.isSearchTextValid) {
|
||||
const parseErrorMsg = intl.formatMessage({
|
||||
id: 'kbn.management.settings.searchBar.unableToParseQueryErrorMessage',
|
||||
defaultMessage: 'Unable to parse query',
|
||||
});
|
||||
queryParseError = (
|
||||
<EuiFormErrorText>
|
||||
{`${parseErrorMsg}. ${this.state.parseErrorMessage}`}
|
||||
</EuiFormErrorText>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiSearchBar
|
||||
box={box}
|
||||
filters={filters}
|
||||
onChange={this.onChange}
|
||||
query={query}
|
||||
/>
|
||||
{queryParseError}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
|
||||
|
||||
import { Query } from '@elastic/eui';
|
||||
|
@ -52,7 +53,32 @@ describe('Search', () => {
|
|||
onQueryChange={onQueryChange}
|
||||
/>
|
||||
);
|
||||
component.find('input').simulate('keyup', { target: { value: 'new filter' } });
|
||||
findTestSubject(component, 'settingsSearchBar').simulate('keyup', { target: { value: 'new filter' } });
|
||||
expect(onQueryChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle query parse error', async () => {
|
||||
const onQueryChangeMock = jest.fn();
|
||||
const component = mountWithIntl(
|
||||
<Search.WrappedComponent
|
||||
query={query}
|
||||
categories={categories}
|
||||
onQueryChange={onQueryChangeMock}
|
||||
/>
|
||||
);
|
||||
|
||||
const searchBar = findTestSubject(component, 'settingsSearchBar');
|
||||
|
||||
// Send invalid query
|
||||
searchBar.simulate('keyup', { target: { value: '?' } });
|
||||
expect(onQueryChangeMock).toHaveBeenCalledTimes(0);
|
||||
expect(component.state().isSearchTextValid).toBe(false);
|
||||
|
||||
onQueryChangeMock.mockReset();
|
||||
|
||||
// Send valid query to ensure component can recover from invalid query
|
||||
searchBar.simulate('keyup', { target: { value: 'dateFormat' } });
|
||||
expect(onQueryChangeMock).toHaveBeenCalledTimes(1);
|
||||
expect(component.state().isSearchTextValid).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue