@@ -122,39 +111,5 @@ export const HeaderExample = {
snippet: headerLinksSnippet,
demo: ,
},
- {
- title: 'Global query and filters',
- source: [
- {
- type: GuideSectionTypes.JS,
- code: globalQuerySource,
- },
- {
- type: GuideSectionTypes.HTML,
- code: globalQueryHtml,
- },
- ],
- text: (
-
-
-
- This documents a visual pattern for the eventual
- replacement of Kibana's global query and filter bars. The
- filter bar has been broken down into multiple components. There
- are still bugs and not all the logic is well-formed.
-
-
-
- ),
- props: {
- GlobalQuery,
- GlobalFilterBar,
- GlobalFilterOptions,
- GlobalFilterAdd,
- GlobalFilterForm,
- GlobalFilterItem,
- },
- demo: ,
- },
],
};
diff --git a/src-docs/src/views/header/_global_filter_group.scss b/src-docs/src/views/suggest/_global_filter_group.scss
similarity index 96%
rename from src-docs/src/views/header/_global_filter_group.scss
rename to src-docs/src/views/suggest/_global_filter_group.scss
index 0af50dadf3a..82908a215aa 100644
--- a/src-docs/src/views/header/_global_filter_group.scss
+++ b/src-docs/src/views/suggest/_global_filter_group.scss
@@ -1,5 +1,5 @@
@import 'global_filter_item';
-@import 'global_filter_form';
+@import 'saved_queries';
.globalFilterGroup__filterBar {
margin-top: $euiSizeM;
@@ -25,4 +25,4 @@
.globalFilterBar__flexItem {
max-width: calc(100% - #{$euiSizeXS}); // Width minus margin around each flex itm
-}
+}
\ No newline at end of file
diff --git a/src-docs/src/views/header/_global_filter_item.scss b/src-docs/src/views/suggest/_global_filter_item.scss
similarity index 100%
rename from src-docs/src/views/header/_global_filter_item.scss
rename to src-docs/src/views/suggest/_global_filter_item.scss
diff --git a/src-docs/src/views/suggest/_saved_queries.scss b/src-docs/src/views/suggest/_saved_queries.scss
new file mode 100644
index 00000000000..a858e74bf36
--- /dev/null
+++ b/src-docs/src/views/suggest/_saved_queries.scss
@@ -0,0 +1,31 @@
+.savedQueriesInput__hideDatepicker {
+ .euiSuperDatePicker__flexWrapper {
+ width: 100%;
+
+ > div:nth-of-type(1) {
+ display: none;
+ }
+ }
+}
+
+.savedQueriesInput {
+ padding-bottom: $euiSizeXL * 6;
+}
+
+.savedQueryManagement__text {
+ padding: $euiSizeM $euiSizeM ($euiSizeM / 2);
+}
+
+.savedQueryManagement__listWrapper {
+ // Addition height will ensure one item is "cutoff" to indicate more below the scroll
+ max-height: $euiFormMaxWidth + $euiSize;
+ overflow-y: hidden;
+}
+
+.savedQueryManagement__list {
+ @include euiYScrollWithShadows;
+ max-height: inherit; // Fixes overflow for applied max-height
+ // Left/Right padding is calculated to match the left alignment of the
+ // popover text and buttons
+ padding: ($euiSizeM / 2) $euiSizeXS !important; // sass-lint:disable-line no-important
+}
\ No newline at end of file
diff --git a/src-docs/src/views/header/global_filter_add.js b/src-docs/src/views/suggest/global_filter_add.js
similarity index 93%
rename from src-docs/src/views/header/global_filter_add.js
rename to src-docs/src/views/suggest/global_filter_add.js
index 7159959b019..26239b98d50 100644
--- a/src-docs/src/views/header/global_filter_add.js
+++ b/src-docs/src/views/suggest/global_filter_add.js
@@ -13,13 +13,9 @@ import GlobalFilterForm from './global_filter_form';
export default class GlobalFilterAdd extends Component {
static propTypes = {};
- constructor(props) {
- super(props);
-
- this.state = {
- isPopoverOpen: false,
- };
- }
+ state = {
+ isPopoverOpen: false,
+ };
togglePopover = () => {
this.setState(prevState => ({
diff --git a/src-docs/src/views/header/global_filter_bar.js b/src-docs/src/views/suggest/global_filter_bar.js
similarity index 100%
rename from src-docs/src/views/header/global_filter_bar.js
rename to src-docs/src/views/suggest/global_filter_bar.js
diff --git a/src-docs/src/views/header/global_filter_form.js b/src-docs/src/views/suggest/global_filter_form.js
similarity index 92%
rename from src-docs/src/views/header/global_filter_form.js
rename to src-docs/src/views/suggest/global_filter_form.js
index 648ae81cd15..7afebabcf94 100644
--- a/src-docs/src/views/header/global_filter_form.js
+++ b/src-docs/src/views/suggest/global_filter_form.js
@@ -75,26 +75,22 @@ export default class GlobalFilterForm extends Component {
selectedObject: PropTypes.object,
};
- constructor(props) {
- super(props);
-
- this.state = {
- fieldOptions: fieldOptions,
- operandOptions: operatorOptions,
- valueOptions: valueOptions,
- selectedField: this.props.selectedObject
- ? this.props.selectedObject.field
- : [],
- selectedOperand: this.props.selectedObject
- ? this.props.selectedObject.operand
- : [],
- selectedValues: this.props.selectedObject
- ? this.props.selectedObject.values
- : [],
- useCustomLabel: false,
- customLabel: '',
- };
- }
+ state = {
+ fieldOptions: fieldOptions,
+ operandOptions: operatorOptions,
+ valueOptions: valueOptions,
+ selectedField: this.props.selectedObject
+ ? this.props.selectedObject.field
+ : [],
+ selectedOperand: this.props.selectedObject
+ ? this.props.selectedObject.operand
+ : [],
+ selectedValues: this.props.selectedObject
+ ? this.props.selectedObject.values
+ : [],
+ useCustomLabel: false,
+ customLabel: '',
+ };
onFieldChange = selectedOptions => {
// We should only get back either 0 or 1 options.
diff --git a/src-docs/src/views/header/global_filter_item.js b/src-docs/src/views/suggest/global_filter_item.js
similarity index 97%
rename from src-docs/src/views/header/global_filter_item.js
rename to src-docs/src/views/suggest/global_filter_item.js
index 941960a72ff..a575f10bccd 100644
--- a/src-docs/src/views/header/global_filter_item.js
+++ b/src-docs/src/views/suggest/global_filter_item.js
@@ -36,13 +36,9 @@ export class GlobalFilterItem extends Component {
isExcluded: PropTypes.bool.isRequired,
};
- constructor(props) {
- super(props);
-
- this.state = {
- isPopoverOpen: false,
- };
- }
+ state = {
+ isPopoverOpen: false,
+ };
togglePopover = () => {
this.setState(prevState => ({
diff --git a/src-docs/src/views/header/global_filter_options.js b/src-docs/src/views/suggest/global_filter_options.js
similarity index 95%
rename from src-docs/src/views/header/global_filter_options.js
rename to src-docs/src/views/suggest/global_filter_options.js
index 00d5162708e..ee0e7a276af 100644
--- a/src-docs/src/views/header/global_filter_options.js
+++ b/src-docs/src/views/suggest/global_filter_options.js
@@ -25,13 +25,9 @@ function flattenPanelTree(tree, array = []) {
export default class GlobalFilterOptions extends Component {
static propTypes = {};
- constructor(props) {
- super(props);
-
- this.state = {
- isPopoverOpen: false,
- };
- }
+ state = {
+ isPopoverOpen: false,
+ };
togglePopover = () => {
this.setState(prevState => ({
@@ -109,7 +105,7 @@ export default class GlobalFilterOptions extends Component {
diff --git a/src-docs/src/views/suggest/hashtag_popover.js b/src-docs/src/views/suggest/hashtag_popover.js
new file mode 100644
index 00000000000..58cdce3f85c
--- /dev/null
+++ b/src-docs/src/views/suggest/hashtag_popover.js
@@ -0,0 +1,106 @@
+import React, { Component } from 'react';
+
+import {
+ EuiButton,
+ EuiButtonEmpty,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIcon,
+ EuiListGroup,
+ EuiListGroupItem,
+ EuiPopover,
+ EuiPopoverFooter,
+ EuiPopoverTitle,
+ EuiText,
+} from '../../../../src/components';
+
+export default class HashtagPopover extends Component {
+ static propTypes = {};
+
+ state = {
+ isPopoverOpen: false,
+ };
+
+ togglePopover = () => {
+ this.setState(prevState => ({
+ isPopoverOpen: !prevState.isPopoverOpen,
+ }));
+ };
+
+ closePopover = () => {
+ this.setState({ isPopoverOpen: false });
+ };
+
+ onButtonClick = () => {
+ this.setState({
+ isPopoverOpen: !this.state.isPopoverOpen,
+ });
+ };
+
+ render() {
+ const { isPopoverOpen } = this.state;
+
+ const hashtagButton = (
+
+
+
+ );
+
+ return (
+
+ SAVED QUERIES
+
+
+ Save query text and filters that you want to use again.
+
+
+
+
+
+
+
+ {this.props.value !== '' ? (
+
+
+
+ Save
+
+
+
+ ) : (
+ undefined
+ )}
+
+
+ );
+ }
+}
diff --git a/src-docs/src/views/suggest/saved_queries.js b/src-docs/src/views/suggest/saved_queries.js
new file mode 100644
index 00000000000..353ec1f925c
--- /dev/null
+++ b/src-docs/src/views/suggest/saved_queries.js
@@ -0,0 +1,163 @@
+import React, { Component } from 'react';
+
+import {
+ EuiButtonEmpty,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSuggest,
+ EuiSuperDatePicker,
+} from '../../../../src/components';
+
+import { GlobalFilterBar } from './global_filter_bar';
+import GlobalFilterOptions from './global_filter_options';
+import HashtagPopover from './hashtag_popover';
+
+const shortDescription = 'This is the description';
+
+const sampleItems = [
+ {
+ type: { iconType: 'kqlField', color: 'tint4' },
+ label: 'Field sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'kqlValue', color: 'tint0' },
+ label: 'Value sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'kqlSelector', color: 'tint2' },
+ label: 'Conjunction sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'kqlOperand', color: 'tint1' },
+ label: 'Operator sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'search', color: 'tint8' },
+ label: 'Recent search',
+ },
+ {
+ type: { iconType: 'save', color: 'tint3' },
+ label: 'Saved search',
+ },
+];
+
+export default class extends Component {
+ state = {
+ status: 'unchanged',
+ value: '',
+ hideDatepicker: false,
+ filters: [
+ {
+ id: 'filter0',
+ field: '@tags.keyword',
+ operator: 'IS',
+ value: 'value',
+ isDisabled: false,
+ isPinned: true,
+ isExcluded: false,
+ },
+ {
+ id: 'filter1',
+ field: '@tags.keyword',
+ operator: 'IS',
+ value: 'value',
+ isDisabled: true,
+ isPinned: false,
+ isExcluded: false,
+ },
+ {
+ id: 'filter2',
+ field: '@tags.keyword',
+ operator: 'IS NOT',
+ value: 'value',
+ isDisabled: false,
+ isPinned: true,
+ isExcluded: true,
+ },
+ {
+ id: 'filter3',
+ field: '@tags.keyword',
+ operator: 'IS',
+ value: 'value',
+ isDisabled: false,
+ isPinned: false,
+ isExcluded: false,
+ },
+ ],
+ };
+
+ onFieldFocus = () => {
+ this.setState({
+ hideDatepicker: true,
+ });
+ };
+
+ onFieldBlur = () => {
+ this.setState({
+ hideDatepicker: false,
+ });
+ };
+
+ getInputValue = val => {
+ this.setState({
+ value: val,
+ });
+ };
+
+ onItemClick = item => {
+ alert(`Item [${item.label}] was clicked`);
+ };
+
+ onTimeChange() {
+ alert('Time changed');
+ }
+
+ render() {
+ const append = KQL;
+
+ return (
+
+
+
+ }
+ append={append}
+ suggestions={sampleItems}
+ onItemClick={this.onItemClick}
+ onInputChange={this.getInputValue}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src-docs/src/views/suggest/suggest.js b/src-docs/src/views/suggest/suggest.js
index 3c4c903427b..b2a0dc3d12e 100644
--- a/src-docs/src/views/suggest/suggest.js
+++ b/src-docs/src/views/suggest/suggest.js
@@ -1,3 +1,101 @@
-import React from 'react';
+import React, { Component } from 'react';
-export default () => ;
+import {
+ EuiRadioGroup,
+ EuiSuggest,
+ EuiSpacer,
+} from '../../../../src/components';
+
+import makeId from '../../../../src/components/form/form_row/make_id';
+
+const shortDescription = 'This is the description';
+
+const sampleItems = [
+ {
+ type: { iconType: 'kqlField', color: 'tint4' },
+ label: 'Field sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'kqlValue', color: 'tint0' },
+ label: 'Value sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'kqlSelector', color: 'tint2' },
+ label: 'Conjunction sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'kqlOperand', color: 'tint1' },
+ label: 'Operator sample',
+ description: shortDescription,
+ },
+ {
+ type: { iconType: 'search', color: 'tint8' },
+ label: 'Recent search',
+ },
+ {
+ type: { iconType: 'save', color: 'tint3' },
+ label: 'Saved search',
+ },
+];
+
+export default class extends Component {
+ constructor(props) {
+ super(props);
+
+ const idPrefix = makeId();
+
+ this.radios = [
+ { id: `${idPrefix}0`, value: 'unchanged', label: 'No new changes' },
+ { id: `${idPrefix}1`, value: 'unsaved', label: 'Not yet saved' },
+ { id: `${idPrefix}2`, value: 'saved', label: 'Saved' },
+ { id: `${idPrefix}3`, value: 'loading', label: 'Loading' },
+ ];
+
+ this.state = {
+ status: 'unchanged',
+ radioIdSelected: `${idPrefix}0`,
+ value: '',
+ tooltipContent: '',
+ };
+ }
+
+ onChange = optionId => {
+ this.setState({
+ radioIdSelected: optionId,
+ status: this.radios.find(x => x.id === optionId).value,
+ });
+ };
+
+ onItemClick(item) {
+ alert(`Item [${item.label}] was clicked`);
+ }
+
+ getInputValue = val => {
+ this.setState({
+ value: val,
+ });
+ };
+
+ render() {
+ return (
+
+
+
+
+
+ );
+ }
+}
diff --git a/src-docs/src/views/suggest/suggest_example.js b/src-docs/src/views/suggest/suggest_example.js
index b884f78846b..3b755e465e2 100644
--- a/src-docs/src/views/suggest/suggest_example.js
+++ b/src-docs/src/views/suggest/suggest_example.js
@@ -4,12 +4,22 @@ import { renderToHtml } from '../../services';
import { GuideSectionTypes } from '../../components';
-import { EuiCode, EuiSuggestItem } from '../../../../src/components';
+import {
+ EuiCallOut,
+ EuiCode,
+ EuiSpacer,
+ EuiSuggest,
+ EuiSuggestItem,
+} from '../../../../src/components';
import Suggest from './suggest';
const suggestSource = require('!!raw-loader!./suggest');
const suggestHtml = renderToHtml(Suggest);
+import SavedQueries from './saved_queries';
+const savedQueriesSource = require('!!raw-loader!./saved_queries');
+const savedQueriesHtml = renderToHtml(SavedQueries);
+
import SuggestItem from './suggest_item';
const suggestItemSource = require('!!raw-loader!./suggest_item');
const suggestItemHtml = renderToHtml(SuggestItem);
@@ -28,6 +38,27 @@ const suggestItemSnippet = [
/>`,
];
+const suggestSnippet = [
+ ``,
+];
+
export const SuggestExample = {
title: 'Suggest',
sections: [
@@ -45,10 +76,17 @@ export const SuggestExample = {
text: (
- EuiSuggest description goes here.
+ EuiSuggest is a text field component used to
+ display suggestions. The status of the component is shown on its
+ right side. The available status are:{' '}
+ unsaved, saved,
+ unchanged and isLoading.
),
+ props: { EuiSuggest },
+ snippet: suggestSnippet,
+ demo: ,
},
{
title: 'Suggest Item',
@@ -67,9 +105,9 @@ export const SuggestExample = {
EuiSuggestItem is a list item component to
display suggestions when typing queries in{' '}
- EuiSuggestInput. Use{' '}
- labelDisplay to set whether the{' '}
- label has a fixed width or not.
+ EuiSuggest. Use labelDisplay{' '}
+ to set whether the label has a fixed width or
+ not.
),
@@ -77,5 +115,33 @@ export const SuggestExample = {
snippet: suggestItemSnippet,
demo: ,
},
+ {
+ title: 'Saved queries and filters',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: savedQueriesSource,
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: savedQueriesHtml,
+ },
+ ],
+ text: (
+
+
+
+ This documents a visual pattern for Kibana's
+ global query and filter bars. The filter bar has been broken down
+ into multiple components. There are still bugs and not all the
+ logic is well-formed.
+
+
+
+
+ ),
+ props: { EuiSuggest },
+ demo: ,
+ },
],
};
diff --git a/src/components/index.js b/src/components/index.js
index 85a5dd2ac8f..5d93ff1ab84 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -236,7 +236,7 @@ export { EuiStat } from './stat';
export { EuiStep, EuiSteps, EuiSubSteps, EuiStepsHorizontal } from './steps';
-export { EuiSuggestItem } from './suggest_item';
+export { EuiSuggestInput, EuiSuggestItem, EuiSuggest } from './suggest';
export {
EuiTable,
diff --git a/src/components/index.scss b/src/components/index.scss
index c39303f5798..82fba518747 100644
--- a/src/components/index.scss
+++ b/src/components/index.scss
@@ -51,7 +51,7 @@
@import 'selectable/index';
@import 'stat/index';
@import 'steps/index';
-@import 'suggest_item/index';
+@import 'suggest/index';
@import 'table/index';
@import 'tabs/index';
@import 'title/index';
diff --git a/src/components/suggest/__snapshots__/suggest.test.js.snap b/src/components/suggest/__snapshots__/suggest.test.js.snap
new file mode 100644
index 00000000000..f98a783343a
--- /dev/null
+++ b/src/components/suggest/__snapshots__/suggest.test.js.snap
@@ -0,0 +1,36 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EuiSuggest is rendered 1`] = `
+
+`;
diff --git a/src/components/suggest/__snapshots__/suggest_input.test.js.snap b/src/components/suggest/__snapshots__/suggest_input.test.js.snap
new file mode 100644
index 00000000000..a3330c560fe
--- /dev/null
+++ b/src/components/suggest/__snapshots__/suggest_input.test.js.snap
@@ -0,0 +1,46 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EuiSuggestInput is rendered 1`] = `
+
+`;
diff --git a/src/components/suggest_item/__snapshots__/suggest_item.test.js.snap b/src/components/suggest/__snapshots__/suggest_item.test.js.snap
similarity index 100%
rename from src/components/suggest_item/__snapshots__/suggest_item.test.js.snap
rename to src/components/suggest/__snapshots__/suggest_item.test.js.snap
diff --git a/src/components/suggest_item/_index.scss b/src/components/suggest/_index.scss
similarity index 63%
rename from src/components/suggest_item/_index.scss
rename to src/components/suggest/_index.scss
index c4dd928bb4e..d31182e14d4 100644
--- a/src/components/suggest_item/_index.scss
+++ b/src/components/suggest/_index.scss
@@ -1,3 +1,5 @@
@import 'variables';
@import 'suggest_item';
+
+@import 'suggest_input';
diff --git a/src/components/suggest/_suggest_input.scss b/src/components/suggest/_suggest_input.scss
new file mode 100644
index 00000000000..ef30a41be16
--- /dev/null
+++ b/src/components/suggest/_suggest_input.scss
@@ -0,0 +1,14 @@
+.euiSuggestInput {
+ font-size: $euiFontSizeS;
+ color: $euiColorPrimary;
+
+ .euiLoadingSpinner {
+ margin-right: $euiSizeS;
+ }
+
+ .euiSuggestInput__statusIcon {
+ padding-left: $euiSizeS;
+ padding-right: $euiSizeS;
+ }
+
+}
diff --git a/src/components/suggest_item/_suggest_item.scss b/src/components/suggest/_suggest_item.scss
similarity index 78%
rename from src/components/suggest_item/_suggest_item.scss
rename to src/components/suggest/_suggest_item.scss
index 9f063b25889..ac5bcc58215 100644
--- a/src/components/suggest_item/_suggest_item.scss
+++ b/src/components/suggest/_suggest_item.scss
@@ -5,13 +5,28 @@
font-size: $euiFontSizeXS;
white-space: nowrap;
- @each $name, $color in $itemColors {
+ &.euiSuggestItem-isClickable {
+ width: 100%;
+ text-align: left;
+
+ &:hover,
+ &:focus {
+ cursor: pointer;
+ background-color: $euiColorLightestShade;
+
+ .euiSuggestItem__type { //sass-lint:disable-line nesting-depth
+ color: $euiColorDarkestShade;
+ }
+ }
+ }
+
+ @each $name, $color in $euiSuggestItemColors {
.euiSuggestItem__type--#{$name} {
$backgroundColor: tintOrShade($color, 90%, 50%);
background-color: $backgroundColor;
color: makeHighContrastColor($color, $backgroundColor);
- }
- }
+ }
+ }
.euiSuggestItem__label,
.euiSuggestItem__type,
@@ -19,7 +34,7 @@
flex-grow: 0;
display: flex;
flex-direction: column;
- }
+ }
.euiSuggestItem__type {
position: relative;
@@ -53,7 +68,7 @@
.euiSuggestItem__label {
@include euiTextTruncate;
display: block;
- }
+ }
.euiSuggestItem__description {
color: $euiColorDarkShade;
@@ -64,5 +79,5 @@
flex-grow: 0;
margin-left: 0;
}
- }
+ }
}
diff --git a/src/components/suggest_item/_variables.scss b/src/components/suggest/_variables.scss
similarity index 89%
rename from src/components/suggest_item/_variables.scss
rename to src/components/suggest/_variables.scss
index d90fa21260d..ff718dea7f6 100644
--- a/src/components/suggest_item/_variables.scss
+++ b/src/components/suggest/_variables.scss
@@ -1,4 +1,4 @@
-$itemColors: (
+$euiSuggestItemColors: (
tint0: $euiColorVis0,
tint1: $euiColorVis1,
tint2: $euiColorVis2,
diff --git a/src/components/suggest/index.js b/src/components/suggest/index.js
new file mode 100644
index 00000000000..2204a3cb211
--- /dev/null
+++ b/src/components/suggest/index.js
@@ -0,0 +1,5 @@
+export { EuiSuggestInput } from './suggest_input';
+
+export { EuiSuggestItem } from './suggest_item';
+
+export { EuiSuggest } from './suggest';
diff --git a/src/components/suggest/suggest.js b/src/components/suggest/suggest.js
new file mode 100644
index 00000000000..0f657f08c6a
--- /dev/null
+++ b/src/components/suggest/suggest.js
@@ -0,0 +1,81 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { EuiSuggestItem } from './suggest_item';
+import { EuiSuggestInput } from './suggest_input';
+
+export class EuiSuggest extends Component {
+ state = {
+ value: '',
+ status: 'unsaved',
+ };
+
+ getValue = val => {
+ this.setState({
+ value: val,
+ });
+ };
+
+ onChange = e => {
+ this.props.onInputChange(e.target.value);
+ };
+
+ render() {
+ const {
+ onItemClick,
+ onInputChange,
+ status,
+ append,
+ tooltipContent,
+ suggestions,
+ ...rest
+ } = this.props;
+
+ const suggestionList = suggestions.map((item, index) => (
+ onItemClick(item) : null}
+ description={item.description}
+ />
+ ));
+
+ const suggestInput = (
+
+ );
+ return {suggestInput}
;
+ }
+}
+
+EuiSuggest.propTypes = {
+ className: PropTypes.string,
+ /**
+ * Status of the current query 'notYetSaved', 'saved', 'unchanged' or 'loading'.
+ */
+ status: PropTypes.oneOf(['unsaved', 'saved', 'unchanged', 'loading']),
+ tooltipContent: PropTypes.string,
+ /**
+ * Element to be appended to the input bar (e.g. hashtag popover).
+ */
+ append: PropTypes.node,
+ /**
+ * List of suggestions to display using 'suggestItem'.
+ */
+ suggestions: PropTypes.array,
+ /**
+ * Handler for click on a suggestItem.
+ */
+ onItemClick: PropTypes.func,
+ onInputChange: PropTypes.func,
+};
+
+EuiSuggestInput.defaultProps = {
+ status: 'unchanged',
+};
diff --git a/src/components/suggest/suggest.test.js b/src/components/suggest/suggest.test.js
new file mode 100644
index 00000000000..69e5a9ccd5f
--- /dev/null
+++ b/src/components/suggest/suggest.test.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../test/required_props';
+
+import { EuiSuggest } from './suggest';
+
+const sampleItems = [
+ {
+ type: { iconType: 'kqlField', color: 'tint4' },
+ label: 'Field sample',
+ description: 'Description',
+ },
+ {
+ type: { iconType: 'kqlValue', color: 'tint0' },
+ label: 'Value sample',
+ description: 'Description',
+ },
+];
+
+describe('EuiSuggest', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/src/components/suggest/suggest_input.js b/src/components/suggest/suggest_input.js
new file mode 100644
index 00000000000..6147b5cc4d3
--- /dev/null
+++ b/src/components/suggest/suggest_input.js
@@ -0,0 +1,132 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { EuiFilterButton } from '../filter_group';
+import { EuiFieldText } from '../form';
+import { EuiToolTip } from '../tool_tip';
+import { EuiIcon } from '../icon';
+import { EuiPopover, EuiInputPopover } from '../popover';
+
+const statusMap = {
+ unsaved: {
+ icon: 'dot',
+ color: 'accent',
+ tooltip: 'Changes have not been saved.',
+ },
+ saved: {
+ icon: 'checkInCircleFilled',
+ color: 'secondary',
+ tooltip: 'Saved.',
+ },
+ unchanged: {
+ icon: '',
+ color: 'secondary',
+ },
+};
+
+export class EuiSuggestInput extends Component {
+ state = {
+ value: '',
+ isPopoverOpen: false,
+ };
+
+ onFieldChange = e => {
+ this.setState({
+ value: e.target.value,
+ isPopoverOpen: e.target.value !== '' ? true : false,
+ });
+ this.props.sendValue(e.target.value);
+ };
+
+ closePopover = () => {
+ this.setState({
+ isPopoverOpen: false,
+ });
+ };
+
+ render() {
+ const {
+ className,
+ status,
+ append,
+ tooltipContent,
+ suggestions,
+ sendValue,
+ ...rest
+ } = this.props;
+
+ let icon;
+ let color;
+
+ if (statusMap[status]) {
+ icon = statusMap[status].icon;
+ color = statusMap[status].color;
+ }
+ const classes = classNames('euiSuggestInput', className);
+
+ // EuiFieldText's append accepts an array of elements so start by creating an empty array
+ const appendArray = [];
+
+ const statusElement = (status === 'saved' || status === 'unsaved') && (
+
+
+
+ );
+
+ // Push the status element to the array if it is not undefined
+ if (statusElement) appendArray.push(statusElement);
+
+ // Check to see if consumer passed an append item and if so, add it to the array
+ if (append) appendArray.push(append);
+
+ const customInput = (
+
+ );
+
+ return (
+
+ );
+ }
+}
+
+EuiSuggestInput.propTypes = {
+ className: PropTypes.string,
+ /**
+ * Status of the current query 'unsaved', 'saved', 'unchanged' or 'loading'.
+ */
+ status: PropTypes.oneOf(['unsaved', 'saved', 'unchanged', 'loading']),
+ tooltipContent: PropTypes.string,
+ /**
+ * Element to be appended to the input bar.
+ */
+ append: PropTypes.node,
+ /**
+ * List of suggestions to display using 'suggestItem'.
+ */
+ suggestions: PropTypes.array,
+};
+
+EuiSuggestInput.defaultProps = {
+ status: 'unchanged',
+};
diff --git a/src/components/suggest/suggest_input.test.js b/src/components/suggest/suggest_input.test.js
new file mode 100644
index 00000000000..d96dae5b097
--- /dev/null
+++ b/src/components/suggest/suggest_input.test.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../test/required_props';
+
+import { EuiSuggestInput } from './suggest_input';
+
+const sampleItems = [
+ {
+ type: { iconType: 'kqlField', color: 'tint4' },
+ label: 'Field sample',
+ description: 'Description',
+ },
+ {
+ type: { iconType: 'kqlValue', color: 'tint0' },
+ label: 'Value sample',
+ description: 'Description',
+ },
+];
+
+describe('EuiSuggestInput', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/src/components/suggest_item/suggest_item.js b/src/components/suggest/suggest_item.js
similarity index 85%
rename from src/components/suggest_item/suggest_item.js
rename to src/components/suggest/suggest_item.js
index 0d396023414..7b07559c936 100644
--- a/src/components/suggest_item/suggest_item.js
+++ b/src/components/suggest/suggest_item.js
@@ -31,9 +31,16 @@ export const EuiSuggestItem = ({
type,
labelDisplay,
description,
+ onClick,
...rest
}) => {
- const classes = classNames('euiSuggestItem', className);
+ const classes = classNames(
+ 'euiSuggestItem',
+ {
+ 'euiSuggestItem-isClickable': onClick,
+ },
+ className
+ );
let colorClass = '';
@@ -51,14 +58,19 @@ export const EuiSuggestItem = ({
}
}
+ let OuterElement = 'div';
+ if (onClick) {
+ OuterElement = 'button';
+ }
+
return (
-
+
{label}
{description}
-
+
);
};
@@ -84,6 +96,10 @@ EuiSuggestItem.propTypes = {
* Label display is 'fixed' by default. Label will increase its width beyond 50% if needed with 'expand'.
*/
labelDisplay: PropTypes.oneOf(DISPLAYS),
+ /**
+ * Handler for click on a suggestItem.
+ */
+ onClick: PropTypes.func,
};
EuiSuggestItem.defaultProps = {
diff --git a/src/components/suggest_item/suggest_item.test.js b/src/components/suggest/suggest_item.test.js
similarity index 100%
rename from src/components/suggest_item/suggest_item.test.js
rename to src/components/suggest/suggest_item.test.js
diff --git a/src/components/suggest_item/index.js b/src/components/suggest_item/index.js
deleted file mode 100644
index fde82176579..00000000000
--- a/src/components/suggest_item/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { EuiSuggestItem } from './suggest_item';