Skip to content

Commit

Permalink
Update EuiSearchBar to React 16.3 lifecycle (elastic#863)
Browse files Browse the repository at this point in the history
* updated EuiSearchBar to React 16.3 lifecycle, refactored how its controlled-value option works

* revert dangling comma removal

* changelog

* Updated EuiSearchBar to better match the controlled component pattern, added example

* some more cleanups
  • Loading branch information
chandlerprall committed May 24, 2018
1 parent 14dd37c commit 18b5b6e
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 46 deletions.
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

0 comments on commit 18b5b6e

Please sign in to comment.