Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## [`master`](https://github.com/elastic/eui/tree/master)

**Breaking changes**

- `EuiSearchBar` no longer has an `onParse` callback, and now passes an object to `onChange` with the shape `{ query, queryText, error }` ([#863](https://github.com/elastic/eui/pull/863))
- `EuiInMemoryTable`'s `search.onChange` callback now passes an object with `{ query, queryText, error }` instead of only the query ([#863](https://github.com/elastic/eui/pull/863))

**Bug fixes**

- `EuiButton`, `EuiButtonEmpty`, and `EuiButtonIcon` now look and behave disabled when `isDisabled={true}` ([#862](https://github.com/elastic/eui/pull/862))
Expand Down
312 changes: 312 additions & 0 deletions src-docs/src/views/search_bar/controlled_search_bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
import React, { Component, Fragment } from 'react';
import { times } from 'lodash';
import { Random } from '../../../../src/services/random';
import {
EuiHealth,
EuiCallOut,
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
EuiSwitch,
EuiBasicTable,
EuiSearchBar,
EuiButton,
} from '../../../../src/components';

const random = new Random();

const tags = [
{ name: 'marketing', color: 'danger' },
{ name: 'finance', color: 'success' },
{ name: 'eng', color: 'success' },
{ name: 'sales', color: 'warning' },
{ name: 'ga', color: 'success' }
];

const types = [
'dashboard',
'visualization',
'watch',
];

const users = [
'dewey',
'wanda',
'carrie',
'jmack',
'gabic',
];

const items = times(10, (id) => {
return {
id,
status: random.oneOf(['open', 'closed']),
type: random.oneOf(types),
tag: random.setOf(tags.map(tag => tag.name), { min: 0, max: 3 }),
active: random.boolean(),
owner: random.oneOf(users),
followers: random.integer({ min: 0, max: 20 }),
comments: random.integer({ min: 0, max: 10 }),
stars: random.integer({ min: 0, max: 5 })
};
});

const loadTags = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(tags.map(tag => ({
value: tag.name,
view: <EuiHealth color={tag.color}>{tag.name}</EuiHealth>
})));
}, 2000);
});
};

const initialQuery = EuiSearchBar.Query.MATCH_ALL;

export class ControlledSearchBar extends Component {

constructor(props) {
super(props);
this.state = {
query: initialQuery,
result: items,
error: null,
incremental: false
};
}

onChange = ({ query, error }) => {
if (error) {
this.setState({ error });
} else {
this.setState({
error: null,
result: EuiSearchBar.Query.execute(query, items, { defaultFields: ['owner', 'tag', 'type'] }),
query
});
}
};

toggleIncremental = () => {
this.setState(prevState => ({ incremental: !prevState.incremental }));
};

setQuery = query => {
this.setState({ query });
}

renderBookmarks() {
return (
<Fragment>
<p>Enter a query, or select one from a bookmark</p>
<EuiSpacer size="s"/>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton size="s" onClick={() => this.setQuery('status:open owner:dewey')}>mine, open</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton size="s" onClick={() => this.setQuery('status:closed owner:dewey')}>mine, closed</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m"/>
</Fragment>
);
}

renderSearch() {
const { incremental } = this.state;

const filters = [
{
type: 'field_value_toggle_group',
field: 'status',
items: [
{
value: 'open',
name: 'Open'
},
{
value: 'closed',
name: 'Closed'
}
]
},
{
type: 'is',
field: 'active',
name: 'Active',
negatedName: 'Inactive'
},
{
type: 'field_value_toggle',
name: 'Mine',
field: 'owner',
value: 'dewey'
},
{
type: 'field_value_selection',
field: 'tag',
name: 'Tag',
multiSelect: 'or',
cache: 10000, // will cache the loaded tags for 10 sec
options: () => loadTags()
}
];

const schema = {
strict: true,
fields: {
active: {
type: 'boolean'
},
status: {
type: 'string'
},
followers: {
type: 'number'
},
comments: {
type: 'number'
},
stars: {
type: 'number'
},
created: {
type: 'date'
},
owner: {
type: 'string'
},
tag: {
type: 'string',
validate: (value) => {
if (!tags.some(tag => tag.name === value)) {
throw new Error(`unknown tag (possible values: ${tags.map(tag => tag.name).join(',')})`);
}
}
}
}
};

return (
<EuiSearchBar
query={this.state.query}
box={{
placeholder: 'e.g. type:visualization -is:active joe',
incremental,
schema
}}
filters={filters}
onChange={this.onChange}
/>
);
}

renderError() {
const { error } = this.state;
if (!error) {
return;
}
return (
<Fragment>
<EuiCallOut
iconType="faceSad"
color="danger"
title={`Invalid search: ${error.message}`}
/>
<EuiSpacer size="l"/>
</Fragment>
);
}

renderTable() {
const columns = [
{
name: 'Type',
field: 'type'
},
{
name: 'Open',
field: 'status',
render: (status) => status === 'open' ? 'Yes' : 'No'
},
{
name: 'Active',
field: 'active',
dataType: 'boolean'
},
{
name: 'Tags',
field: 'tag'
},
{
name: 'Owner',
field: 'owner'
},
{
name: 'Stats',
width: '150px',
render: (item) => {
return (
<div>
<div>{`${item.stars} Stars`}</div>
<div>{`${item.followers} Followers`}</div>
<div>{`${item.comments} Comments`}</div>
</div>
);
}
}
];

const queriedItems = EuiSearchBar.Query.execute(this.state.query, items, {
defaultFields: ['owner', 'tag', 'type']
});

return (
<EuiBasicTable
items={queriedItems}
columns={columns}
/>
);
}

render() {
const {
incremental,
} = this.state;

const content = this.renderError() || (
<EuiFlexGroup>
<EuiFlexItem grow={6}>
{this.renderTable()}
</EuiFlexItem>
</EuiFlexGroup>
);

return (
<Fragment>
<EuiFlexGroup>
<EuiFlexItem>
{this.renderBookmarks()}
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
{this.renderSearch()}
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiSwitch
label="Incremental"
checked={incremental}
onChange={this.toggleIncremental}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="l"/>
{content}
</Fragment>
);
}
}
7 changes: 1 addition & 6 deletions src-docs/src/views/search_bar/props_info.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@ export const propsInfo = {
__docgenInfo: {
props: {
onChange: {
description: 'Called every time the query behind the search bar changes',
required: true,
type: { name: '(query: #Query) => void' }
},
onParse: {
description: 'Called every time the text query in the search box is parsed. When parsing is successful ' +
'the callback will receive both the query text and the parsed query. When it fails ' +
'the callback ill receive the query text and an error object (holding the error message)',
required: false,
required: true,
type: { name: '({ query?: #Query, queryText: string, error?: { message: string } }) => void' }
},
query: {
Expand Down
21 changes: 10 additions & 11 deletions src-docs/src/views/search_bar/search_bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,16 @@ export class SearchBar extends Component {
};
}

onParse = ({ error }) => {
this.setState({ error });
};

onChange = (query) => {
this.setState({
error: null,
result: EuiSearchBar.Query.execute(query, items, { defaultFields: ['owner', 'tag', 'type'] }),
query
});
onChange = ({ query, error }) => {
if (error) {
this.setState({ error });
} else {
this.setState({
error: null,
result: EuiSearchBar.Query.execute(query, items, { defaultFields: ['owner', 'tag', 'type'] }),
query
});
}
};

toggleIncremental = () => {
Expand Down Expand Up @@ -178,7 +178,6 @@ export class SearchBar extends Component {
}}
filters={filters}
onChange={this.onChange}
onParse={this.onParse}
/>
);
}
Expand Down
Loading