Skip to content

Commit

Permalink
Introducing EuiSearchBar (#379)
Browse files Browse the repository at this point in the history
Added EuiSearchBar

A powerful search bar for common use cases that require more than just prefix and terms lookups. This one also supports field clauses (e.g. `tag:bug`) and is clauses (e.g. `is:open`).

The bar has a search box where the user can enter the query text and can also hold filters. Filter are essentially UI controls that help to manipulate/build the query - they mainly help those the don't remember the query syntax.
  • Loading branch information
uboness authored Feb 15, 2018
1 parent aff62f7 commit e2d1328
Show file tree
Hide file tree
Showing 71 changed files with 5,472 additions and 69 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Added importAction and exportAction icons ([#394](https://github.com/elastic/eui/pull/394))
- Added `EuiCard` for UI patterns that need an icon/image, title and description with some sort of action. ([#380](https://github.com/elastic/eui/pull/380))
- Add TypeScript definitions for the `<EuiHealth>` component. ([#403](https://github.com/elastic/eui/pull/403))
- Added `SearchBar` component - introduces a simple yet rich query language to search for objects + search box and filter controls to construct/manipulate it. ([#379](https://github.com/elastic/eui/pull/379))

**Bug fixes**

Expand Down
2 changes: 1 addition & 1 deletion docs/bundle.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"moment": "2.13.0",
"node-sass": "^4.5.3",
"npm-run": "^4.1.2",
"pegjs": "^0.9.0",
"postcss-cli": "^4.1.1",
"postcss-inline-svg": "^3.0.0",
"postcss-loader": "^2.0.8",
Expand Down
45 changes: 22 additions & 23 deletions src-docs/src/components/guide_section/guide_section.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,27 @@ import {
EuiLink
} from '../../../../src/components';

function markup(text) {
const regex = /(#[a-zA-Z]+)|(`[^`]+`)/g;
return text.split(regex).map((token, index) => {
if (!token) {
return '';
}
if (token.startsWith('#')) {
const id = token.substring(1);
const onClick = () => {
document.getElementById(id).scrollIntoView();
};
return <EuiLink key={`markup-${index}`} onClick={onClick}>{id}</EuiLink>;
}
if (token.startsWith('`')) {
const code = token.substring(1, token.length - 1);
return <EuiCode key={`markup-${index}`}>{code}</EuiCode>;
}
return token;

});
}

const humanizeType = type => {
if (!type) {
Expand Down Expand Up @@ -170,28 +191,6 @@ export class GuideSection extends Component {

const humanizedType = humanizeType(type);

function markup(text) {
const regex = /(#[a-zA-Z]+)|(`[^`]+`)/g;
return text.split(regex).map((token, index) => {
if (!token) {
return '';
}
if (token.startsWith('#')) {
const id = token.substring(1);
const onClick = () => {
document.getElementById(id).scrollIntoView();
};
return <EuiLink key={`markup-${index}`} onClick={onClick}>{id}</EuiLink>;
}
if (token.startsWith('`')) {
const code = token.substring(1, token.length - 1);
return <EuiCode key={`markup-${index}`}>{code}</EuiCode>;
}
return token;

});
}

const typeMarkup = markup(humanizedType);
const descriptionMarkup = markup(propDescription);
let defaultValueMarkup = '';
Expand Down Expand Up @@ -238,7 +237,7 @@ export class GuideSection extends Component {
descriptionElement = (
<div key={`description-${componentName}`}>
<EuiText>
<p>{description}</p>
<p>{markup(description)}</p>
</EuiText>
<EuiSpacer size="m" key={`propsSpacer-${componentName}`} />
</div>
Expand Down
8 changes: 8 additions & 0 deletions src-docs/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ import { ErrorBoundaryExample }
import { ExpressionExample }
from './views/expression/expression_example';

import { FilterGroupExample }
from './views/filter_group/filter_group_example';

import { FlexExample }
from './views/flex/flex_example';

Expand Down Expand Up @@ -127,6 +130,9 @@ import { PopoverExample }
import { ProgressExample }
from './views/progress/progress_example';

import { SearchBarExample }
from './views/search_bar/search_bar_example';

import { SideNavExample }
from './views/side_nav/side_nav_example';

Expand Down Expand Up @@ -223,6 +229,7 @@ const components = [
DescriptionListExample,
ErrorBoundaryExample,
ExpressionExample,
FilterGroupExample,
FlexExample,
FlyoutExample,
FormExample,
Expand All @@ -241,6 +248,7 @@ const components = [
PanelExample,
PopoverExample,
ProgressExample,
SearchBarExample,
SideNavExample,
SpacerExample,
StepsExample,
Expand Down
129 changes: 129 additions & 0 deletions src-docs/src/views/filter_group/filter_group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@

import React, {
Component,
} from 'react';

import {
EuiPopover,
EuiPopoverTitle,
EuiFieldSearch,
EuiFilterSelectItem,
EuiLoadingChart,
EuiSpacer,
EuiIcon,
EuiFilterGroup,
EuiFilterButton,
} from '../../../../src/components';

export default class extends Component {
constructor(props) {
super(props);

this.state = {
isPopoverOpen: false,
};
}

onButtonClick() {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen,
});
}

closePopover() {
this.setState({
isPopoverOpen: false,
});
}

render() {

const items = [
{ name: 'Johann Sebastian Bach', checked: 'on' },
{ name: 'Wolfgang Amadeus Mozart', checked: 'on' },
{ name: 'Antonín Dvořák', checked: 'off' },
{ name: 'Dmitri Shostakovich' },
{ name: 'Felix Mendelssohn-Bartholdy' },
{ name: 'Franz Liszt' },
{ name: 'Franz Schubert' },
{ name: 'Frédéric Chopin' },
{ name: 'Georg Friedrich Händel' },
{ name: 'Giuseppe Verdi' },
{ name: 'Gustav Mahler' },
{ name: 'Igor Stravinsky' },
{ name: 'Johannes Brahms' },
{ name: 'Joseph Haydn' },
{ name: 'Ludwig van Beethoven' },
{ name: 'Piotr Illitch Tchaïkovsky' },
{ name: 'Robert Schumann' },
{ name: 'Sergej S. Prokofiew' },
{ name: 'Wolfgang Amadeus Mozart' },
];

const button = (
<EuiFilterButton
iconType="arrowDown"
onClick={this.onButtonClick.bind(this)}
isSelected={this.state.isPopoverOpen}
hasActiveFilters={true}
>
Composers
</EuiFilterButton>
);

return (
<EuiFilterGroup>
<EuiFilterButton>
Filter on
</EuiFilterButton>
<EuiFilterButton>
Filter off
</EuiFilterButton>
<EuiPopover
id="popover"
ownFocus
button={button}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover.bind(this)}
panelPaddingSize="none"
withTitle
panelClassName="euiFilterGroup__popoverPanel"
>
<EuiPopoverTitle>
<EuiFieldSearch />
</EuiPopoverTitle>
<div className="euiFilterSelect__items">
{items.map((item, index) => (
<EuiFilterSelectItem
checked={item.checked}
key={index}
>
{item.name}
</EuiFilterSelectItem>
))}
{/*
Use when loading items initially
*/}
<div className="euiFilterSelect__note">
<div className="euiFilterSelect__noteContent">
<EuiLoadingChart size="m" />
<EuiSpacer size="xs" />
<p>Loading filters</p>
</div>
</div>
{/*
Use when no results are returned
*/}
<div className="euiFilterSelect__note">
<div className="euiFilterSelect__noteContent">
<EuiIcon type="minusInCircle" />
<EuiSpacer size="xs" />
<p>No filters found</p>
</div>
</div>
</div>
</EuiPopover>
</EuiFilterGroup>
);
}
}
65 changes: 65 additions & 0 deletions src-docs/src/views/filter_group/filter_group_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';

import { renderToHtml } from '../../services';

import {
GuideSectionTypes,
} from '../../components';

import {
EuiCallOut,
EuiCode,
EuiFilterGroup,
EuiFilterButton,
EuiFilterSelectItem,
EuiSpacer,
} from '../../../../src/components';

import FilterGroup from './filter_group';
const filterGroupSource = require('!!raw-loader!./filter_group');
const filterGroupHtml = renderToHtml(FilterGroup);

export const FilterGroupExample = {
title: 'FilterGroup',
intro: (
<div>
<EuiCallOut
title="Demo of visual pattern only"
color="warning"
>
<p>
This documents a visual pattern used for filtering (usually page heads next to search).
The individual components themselves are very simple
and do not have much functionality on their own. If you are looking for expanded usage
examples please check out the Table of Records component which uses this more fully and
can give you a better example of its usage when applied to filtering.
</p>
</EuiCallOut>

<EuiSpacer size="l" />
</div>
),
sections: [{
title: 'FilterGroup',
source: [{
type: GuideSectionTypes.JS,
code: filterGroupSource,
}, {
type: GuideSectionTypes.HTML,
code: filterGroupHtml,
}],
text: (
<p>
Use <EuiCode>EuiFilterGroup</EuiCode> to wrap <EuiCode>EuiFilterButton</EuiCode>s
into a container that looks nice against form fields (like search). These buttons
are used in two different patterns. The first, as a simple on/off pattern to show
whether a setting is on. The second is as delivery for a popover for filtering an
array of passed items. This mostly uses standard popover mechanics, but the
component <EuiCode>EuiFilterSelectItem</EuiCode> is used for the items themselves.
</p>
),
components: { EuiFilterGroup },
props: { EuiFilterGroup, EuiFilterButton, EuiFilterSelectItem },
demo: <FilterGroup />,
}],
};
Loading

0 comments on commit e2d1328

Please sign in to comment.