From fae0009da1dbe0aa19d48dc3dc0f41d391756bc5 Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Mon, 6 Dec 2021 08:59:21 -0800 Subject: [PATCH 1/8] Add ability to search on index fields Signed-off-by: Abbas Hussain --- .../components/data_tab/field_selector.scss | 15 + .../components/data_tab/field_selector.tsx | 62 +++- .../components/data_tab/lib/field_filter.ts | 91 +++++ .../data_tab/wizard_field_search.tsx | 314 ++++++++++++++++++ 4 files changed, 470 insertions(+), 12 deletions(-) create mode 100644 src/plugins/wizard/public/application/components/data_tab/lib/field_filter.ts create mode 100644 src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx diff --git a/src/plugins/wizard/public/application/components/data_tab/field_selector.scss b/src/plugins/wizard/public/application/components/data_tab/field_selector.scss index c05f75457b0b..da20b278d4ae 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_selector.scss +++ b/src/plugins/wizard/public/application/components/data_tab/field_selector.scss @@ -8,3 +8,18 @@ .wizFieldSelector__fieldGroups { overflow-y: auto; } + +.wizFieldSearch__toggleButton { + width: calc(100% - #{$euiSizeS}); + color: $euiColorPrimary; + padding-left: $euiSizeXS; + margin-left: $euiSizeXS; +} + +.wizFieldSearch__filterWrapper { + flex-grow: 0; +} + +.wizFieldSearch__formWrapper { + padding: $euiSizeM; +} diff --git a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx index 53d385944966..da9375331e0a 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx @@ -3,16 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; - -import { - EuiFormLabel, - EuiFlexItem, - EuiAccordion, - EuiSpacer, - EuiNotificationBadge, - EuiTitle, -} from '@elastic/eui'; +import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import { EuiFlexItem, EuiAccordion, EuiSpacer, EuiNotificationBadge, EuiTitle } from '@elastic/eui'; +import { getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; +import { WizardFieldSearch } from './wizard_field_search'; import { IndexPatternField, @@ -41,7 +35,25 @@ const META_FIELDS: string[] = [ ]; export const FieldSelector = ({ indexFields }: FieldSelectorDeps) => { - const fields = indexFields?.reduce( + const [fieldFilterState, setFieldFilterState] = useState(getDefaultFieldFilter()); + const [filteredFields, setFilteredFields] = useState(indexFields); + + useEffect(() => { + const filteredSubset = indexFields.filter((field) => { + if (!field.displayName.includes(fieldFilterState.name)) { + return false; + } + + // Other conditions + + return true; + }); + + setFilteredFields(filteredSubset); + return; + }, [indexFields, fieldFilterState]); + + const fields = filteredFields?.reduce( (fieldGroups, currentField) => { const category = getFieldCategory(currentField); fieldGroups[category].push(currentField); @@ -55,10 +67,36 @@ export const FieldSelector = ({ indexFields }: FieldSelectorDeps) => { } ); + const onChangeFieldSearch = useCallback( + (field: string, value: string | boolean | undefined) => { + const newState = setFieldFilterProp(fieldFilterState, field, value); + setFieldFilterState(newState); + }, + [fieldFilterState] + ); + + const fieldTypes = useMemo(() => { + const result = ['any']; + if (Array.isArray(fields)) { + for (const field of fields) { + if (result.indexOf(field.type) === -1) { + result.push(field.type); + } + } + } + return result; + }, [fields]); + return (
- TODO: Search goes here +
+ +
+): boolean { + const matchFilter = filterState.type === 'any' || field.type === filterState.type; + const isAggregatable = + filterState.aggregatable === null || field.aggregatable === filterState.aggregatable; + const isSearchable = + filterState.searchable === null || field.searchable === filterState.searchable; + const scriptedOrMissing = + !filterState.missing || + field.type === '_source' || + field.scripted || + fieldCounts[field.name] > 0; + const matchName = !filterState.name || field.name.indexOf(filterState.name) !== -1; + + return matchFilter && isAggregatable && isSearchable && scriptedOrMissing && matchName; +} diff --git a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx new file mode 100644 index 000000000000..e122c9464af5 --- /dev/null +++ b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx @@ -0,0 +1,314 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ +import React, { OptionHTMLAttributes, ReactNode, useState } from 'react'; +import { i18n } from '@osd/i18n'; +import { + EuiFacetButton, + EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPopover, + EuiPopoverFooter, + EuiPopoverTitle, + EuiSelect, + EuiSwitch, + EuiSwitchEvent, + EuiForm, + EuiFormRow, + EuiButtonGroup, + EuiOutsideClickDetector, +} from '@elastic/eui'; +import { FormattedMessage } from '@osd/i18n/react'; + +export interface State { + searchable: string; + aggregatable: string; + type: string; + missing: boolean; + [index: string]: string | boolean; +} + +export interface Props { + /** + * triggered on input of user into search field + */ + onChange: (field: string, value: string | boolean | undefined) => void; + + /** + * the input value of the user + */ + value?: string; + + /** + * types for the type filter + */ + types: string[]; +} + +/** + * Component is Wizard's side bar to search of available fields + * Additionally there's a button displayed that allows the user to show/hide more filter fields + */ +export function WizardFieldSearch({ onChange, value, types }: Props) { + const searchPlaceholder = i18n.translate('wizard.fieldChooser.searchPlaceHolder', { + defaultMessage: 'Search field names', + }); + const aggregatableLabel = i18n.translate('wizard.fieldChooser.filter.aggregatableLabel', { + defaultMessage: 'Aggregatable', + }); + const searchableLabel = i18n.translate('wizard.fieldChooser.filter.searchableLabel', { + defaultMessage: 'Searchable', + }); + const typeLabel = i18n.translate('wizard.fieldChooser.filter.typeLabel', { + defaultMessage: 'Type', + }); + const typeOptions = types + ? types.map((type) => { + return { value: type, text: type }; + }) + : [{ value: 'any', text: 'any' }]; + + const [activeFiltersCount, setActiveFiltersCount] = useState(0); + const [isPopoverOpen, setPopoverOpen] = useState(false); + const [values, setValues] = useState({ + searchable: 'any', + aggregatable: 'any', + type: 'any', + missing: true, + }); + + if (typeof value !== 'string') { + // at initial rendering value is undefined (angular related), this catches the warning + // should be removed once all is react + return null; + } + + const filterBtnAriaLabel = isPopoverOpen + ? i18n.translate('wizard.fieldChooser.toggleFieldFilterButtonHideAriaLabel', { + defaultMessage: 'Hide field filter settings', + }) + : i18n.translate('wizard.fieldChooser.toggleFieldFilterButtonShowAriaLabel', { + defaultMessage: 'Show field filter settings', + }); + + const handleFacetButtonClicked = () => { + setPopoverOpen(!isPopoverOpen); + }; + + const applyFilterValue = (id: string, filterValue: string | boolean) => { + switch (filterValue) { + case 'any': + if (id !== 'type') { + onChange(id, undefined); + } else { + onChange(id, filterValue); + } + break; + case 'true': + onChange(id, true); + break; + case 'false': + onChange(id, false); + break; + default: + onChange(id, filterValue); + } + }; + + const isFilterActive = (name: string, filterValue: string | boolean) => { + return name !== 'missing' && filterValue !== 'any'; + }; + + const handleValueChange = (name: string, filterValue: string | boolean) => { + const previousValue = values[name]; + updateFilterCount(name, previousValue, filterValue); + const updatedValues = { ...values }; + updatedValues[name] = filterValue; + setValues(updatedValues); + applyFilterValue(name, filterValue); + }; + + const updateFilterCount = ( + name: string, + previousValue: string | boolean, + currentValue: string | boolean + ) => { + const previouslyFilterActive = isFilterActive(name, previousValue); + const filterActive = isFilterActive(name, currentValue); + const diff = Number(filterActive) - Number(previouslyFilterActive); + setActiveFiltersCount(activeFiltersCount + diff); + }; + + const handleMissingChange = (e: EuiSwitchEvent) => { + const missingValue = e.target.checked; + handleValueChange('missing', missingValue); + }; + + const buttonContent = ( + } + isSelected={activeFiltersCount > 0} + quantity={activeFiltersCount} + onClick={handleFacetButtonClicked} + > + + + ); + + const select = ( + id: string, + selectOptions: Array<{ text: ReactNode } & OptionHTMLAttributes>, + selectValue: string + ) => { + return ( + ) => + handleValueChange(id, e.target.value) + } + aria-label={i18n.translate('wizard.fieldChooser.filter.fieldSelectorLabel', { + defaultMessage: 'Selection of {id} filter options', + values: { id }, + })} + data-test-subj={`${id}Select`} + compressed + /> + ); + }; + + const toggleButtons = (id: string) => { + return [ + { + id: `${id}-any`, + label: 'any', + }, + { + id: `${id}-true`, + label: 'yes', + }, + { + id: `${id}-false`, + label: 'no', + }, + ]; + }; + + const buttonGroup = (id: string, legend: string) => { + return ( + handleValueChange(id, optionId.replace(`${id}-`, ''))} + buttonSize="compressed" + isFullWidth + data-test-subj={`${id}ButtonGroup`} + /> + ); + }; + + const selectionPanel = ( +
+ + + {buttonGroup('aggregatable', aggregatableLabel)} + + + {buttonGroup('searchable', searchableLabel)} + + + {select('type', typeOptions, values.type)} + + +
+ ); + + return ( + + + + onChange('name', event.currentTarget.value)} + placeholder={searchPlaceholder} + value={value} + /> + + +
+ {}} isDisabled={!isPopoverOpen}> + { + setPopoverOpen(false); + }} + button={buttonContent} + > + + {i18n.translate('wizard.fieldChooser.filter.filterByTypeLabel', { + defaultMessage: 'Filter by type', + })} + + {selectionPanel} + + + + + +
+
+ ); +} From 85e64e5dc66bb92dfbe91ee4859c8b5ae668aed4 Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Mon, 6 Dec 2021 09:37:16 -0800 Subject: [PATCH 2/8] Remove redundant filter state Signed-off-by: Abbas Hussain --- .../components/data_tab/field_selector.scss | 15 -- .../components/data_tab/field_selector.tsx | 39 +-- .../components/data_tab/lib/field_filter.ts | 91 ------- .../data_tab/wizard_field_search.tsx | 236 +----------------- 4 files changed, 13 insertions(+), 368 deletions(-) delete mode 100644 src/plugins/wizard/public/application/components/data_tab/lib/field_filter.ts diff --git a/src/plugins/wizard/public/application/components/data_tab/field_selector.scss b/src/plugins/wizard/public/application/components/data_tab/field_selector.scss index da20b278d4ae..c05f75457b0b 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_selector.scss +++ b/src/plugins/wizard/public/application/components/data_tab/field_selector.scss @@ -8,18 +8,3 @@ .wizFieldSelector__fieldGroups { overflow-y: auto; } - -.wizFieldSearch__toggleButton { - width: calc(100% - #{$euiSizeS}); - color: $euiColorPrimary; - padding-left: $euiSizeXS; - margin-left: $euiSizeXS; -} - -.wizFieldSearch__filterWrapper { - flex-grow: 0; -} - -.wizFieldSearch__formWrapper { - padding: $euiSizeM; -} diff --git a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx index da9375331e0a..e57ec5f3fe1e 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx @@ -3,9 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import React, { useCallback, useState, useEffect } from 'react'; import { EuiFlexItem, EuiAccordion, EuiSpacer, EuiNotificationBadge, EuiTitle } from '@elastic/eui'; -import { getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; import { WizardFieldSearch } from './wizard_field_search'; import { @@ -35,23 +34,21 @@ const META_FIELDS: string[] = [ ]; export const FieldSelector = ({ indexFields }: FieldSelectorDeps) => { - const [fieldFilterState, setFieldFilterState] = useState(getDefaultFieldFilter()); const [filteredFields, setFilteredFields] = useState(indexFields); + const [fieldSearchValue, setFieldSearchValue] = useState(''); useEffect(() => { const filteredSubset = indexFields.filter((field) => { - if (!field.displayName.includes(fieldFilterState.name)) { + if (!field.displayName.includes(fieldSearchValue)) { return false; } - // Other conditions - return true; }); setFilteredFields(filteredSubset); return; - }, [indexFields, fieldFilterState]); + }, [indexFields, fieldSearchValue]); const fields = filteredFields?.reduce( (fieldGroups, currentField) => { @@ -67,35 +64,15 @@ export const FieldSelector = ({ indexFields }: FieldSelectorDeps) => { } ); - const onChangeFieldSearch = useCallback( - (field: string, value: string | boolean | undefined) => { - const newState = setFieldFilterProp(fieldFilterState, field, value); - setFieldFilterState(newState); - }, - [fieldFilterState] - ); - - const fieldTypes = useMemo(() => { - const result = ['any']; - if (Array.isArray(fields)) { - for (const field of fields) { - if (result.indexOf(field.type) === -1) { - result.push(field.type); - } - } - } - return result; - }, [fields]); + const onChangeFieldSearch = useCallback((field: string) => { + setFieldSearchValue(field); + }, []); return (
- +
diff --git a/src/plugins/wizard/public/application/components/data_tab/lib/field_filter.ts b/src/plugins/wizard/public/application/components/data_tab/lib/field_filter.ts deleted file mode 100644 index 510f2682e327..000000000000 --- a/src/plugins/wizard/public/application/components/data_tab/lib/field_filter.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -import { IndexPatternField } from '../../../../../../data/public'; - -export interface FieldFilterState { - missing: boolean; - type: string; - name: string; - aggregatable: null | boolean; - searchable: null | boolean; -} - -export function getDefaultFieldFilter(): FieldFilterState { - return { - missing: true, - type: 'any', - name: '', - aggregatable: null, - searchable: null, - }; -} - -export function setFieldFilterProp( - state: FieldFilterState, - name: string, - value: string | boolean | null | undefined -): FieldFilterState { - const newState = { ...state }; - if (name === 'missing') { - newState.missing = Boolean(value); - } else if (name === 'aggregatable') { - newState.aggregatable = typeof value !== 'boolean' ? null : value; - } else if (name === 'searchable') { - newState.searchable = typeof value !== 'boolean' ? null : value; - } else if (name === 'name') { - newState.name = String(value); - } else if (name === 'type') { - newState.type = String(value); - } - return newState; -} - -export function isFieldFiltered( - field: IndexPatternField, - filterState: FieldFilterState, - fieldCounts: Record -): boolean { - const matchFilter = filterState.type === 'any' || field.type === filterState.type; - const isAggregatable = - filterState.aggregatable === null || field.aggregatable === filterState.aggregatable; - const isSearchable = - filterState.searchable === null || field.searchable === filterState.searchable; - const scriptedOrMissing = - !filterState.missing || - field.type === '_source' || - field.scripted || - fieldCounts[field.name] > 0; - const matchName = !filterState.name || field.name.indexOf(filterState.name) !== -1; - - return matchFilter && isAggregatable && isSearchable && scriptedOrMissing && matchName; -} diff --git a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx index e122c9464af5..32363e837b68 100644 --- a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx @@ -29,26 +29,9 @@ * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ -import React, { OptionHTMLAttributes, ReactNode, useState } from 'react'; +import React from 'react'; import { i18n } from '@osd/i18n'; -import { - EuiFacetButton, - EuiFieldSearch, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPopover, - EuiPopoverFooter, - EuiPopoverTitle, - EuiSelect, - EuiSwitch, - EuiSwitchEvent, - EuiForm, - EuiFormRow, - EuiButtonGroup, - EuiOutsideClickDetector, -} from '@elastic/eui'; -import { FormattedMessage } from '@osd/i18n/react'; +import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; export interface State { searchable: string; @@ -62,50 +45,22 @@ export interface Props { /** * triggered on input of user into search field */ - onChange: (field: string, value: string | boolean | undefined) => void; + onChange: (field: string) => void; /** * the input value of the user */ value?: string; - - /** - * types for the type filter - */ - types: string[]; } /** * Component is Wizard's side bar to search of available fields * Additionally there's a button displayed that allows the user to show/hide more filter fields */ -export function WizardFieldSearch({ onChange, value, types }: Props) { +export function WizardFieldSearch({ onChange, value }: Props) { const searchPlaceholder = i18n.translate('wizard.fieldChooser.searchPlaceHolder', { defaultMessage: 'Search field names', }); - const aggregatableLabel = i18n.translate('wizard.fieldChooser.filter.aggregatableLabel', { - defaultMessage: 'Aggregatable', - }); - const searchableLabel = i18n.translate('wizard.fieldChooser.filter.searchableLabel', { - defaultMessage: 'Searchable', - }); - const typeLabel = i18n.translate('wizard.fieldChooser.filter.typeLabel', { - defaultMessage: 'Type', - }); - const typeOptions = types - ? types.map((type) => { - return { value: type, text: type }; - }) - : [{ value: 'any', text: 'any' }]; - - const [activeFiltersCount, setActiveFiltersCount] = useState(0); - const [isPopoverOpen, setPopoverOpen] = useState(false); - const [values, setValues] = useState({ - searchable: 'any', - aggregatable: 'any', - type: 'any', - missing: true, - }); if (typeof value !== 'string') { // at initial rendering value is undefined (angular related), this catches the warning @@ -113,154 +68,6 @@ export function WizardFieldSearch({ onChange, value, types }: Props) { return null; } - const filterBtnAriaLabel = isPopoverOpen - ? i18n.translate('wizard.fieldChooser.toggleFieldFilterButtonHideAriaLabel', { - defaultMessage: 'Hide field filter settings', - }) - : i18n.translate('wizard.fieldChooser.toggleFieldFilterButtonShowAriaLabel', { - defaultMessage: 'Show field filter settings', - }); - - const handleFacetButtonClicked = () => { - setPopoverOpen(!isPopoverOpen); - }; - - const applyFilterValue = (id: string, filterValue: string | boolean) => { - switch (filterValue) { - case 'any': - if (id !== 'type') { - onChange(id, undefined); - } else { - onChange(id, filterValue); - } - break; - case 'true': - onChange(id, true); - break; - case 'false': - onChange(id, false); - break; - default: - onChange(id, filterValue); - } - }; - - const isFilterActive = (name: string, filterValue: string | boolean) => { - return name !== 'missing' && filterValue !== 'any'; - }; - - const handleValueChange = (name: string, filterValue: string | boolean) => { - const previousValue = values[name]; - updateFilterCount(name, previousValue, filterValue); - const updatedValues = { ...values }; - updatedValues[name] = filterValue; - setValues(updatedValues); - applyFilterValue(name, filterValue); - }; - - const updateFilterCount = ( - name: string, - previousValue: string | boolean, - currentValue: string | boolean - ) => { - const previouslyFilterActive = isFilterActive(name, previousValue); - const filterActive = isFilterActive(name, currentValue); - const diff = Number(filterActive) - Number(previouslyFilterActive); - setActiveFiltersCount(activeFiltersCount + diff); - }; - - const handleMissingChange = (e: EuiSwitchEvent) => { - const missingValue = e.target.checked; - handleValueChange('missing', missingValue); - }; - - const buttonContent = ( - } - isSelected={activeFiltersCount > 0} - quantity={activeFiltersCount} - onClick={handleFacetButtonClicked} - > - - - ); - - const select = ( - id: string, - selectOptions: Array<{ text: ReactNode } & OptionHTMLAttributes>, - selectValue: string - ) => { - return ( - ) => - handleValueChange(id, e.target.value) - } - aria-label={i18n.translate('wizard.fieldChooser.filter.fieldSelectorLabel', { - defaultMessage: 'Selection of {id} filter options', - values: { id }, - })} - data-test-subj={`${id}Select`} - compressed - /> - ); - }; - - const toggleButtons = (id: string) => { - return [ - { - id: `${id}-any`, - label: 'any', - }, - { - id: `${id}-true`, - label: 'yes', - }, - { - id: `${id}-false`, - label: 'no', - }, - ]; - }; - - const buttonGroup = (id: string, legend: string) => { - return ( - handleValueChange(id, optionId.replace(`${id}-`, ''))} - buttonSize="compressed" - isFullWidth - data-test-subj={`${id}ButtonGroup`} - /> - ); - }; - - const selectionPanel = ( -
- - - {buttonGroup('aggregatable', aggregatableLabel)} - - - {buttonGroup('searchable', searchableLabel)} - - - {select('type', typeOptions, values.type)} - - -
- ); - return ( @@ -270,45 +77,12 @@ export function WizardFieldSearch({ onChange, value, types }: Props) { data-test-subj="fieldFilterSearchInput" compressed fullWidth - onChange={(event) => onChange('name', event.currentTarget.value)} + onChange={(event) => onChange(event.currentTarget.value)} placeholder={searchPlaceholder} value={value} /> -
- {}} isDisabled={!isPopoverOpen}> - { - setPopoverOpen(false); - }} - button={buttonContent} - > - - {i18n.translate('wizard.fieldChooser.filter.filterByTypeLabel', { - defaultMessage: 'Filter by type', - })} - - {selectionPanel} - - - - - -
); } From 8f9b3116844d1d0fbdecc97598675fbfd8c158a5 Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Mon, 6 Dec 2021 10:22:34 -0800 Subject: [PATCH 3/8] Remove unused interface Signed-off-by: Abbas Hussain --- .../components/data_tab/wizard_field_search.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx index 32363e837b68..220835d35140 100644 --- a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx @@ -33,14 +33,6 @@ import React from 'react'; import { i18n } from '@osd/i18n'; import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -export interface State { - searchable: string; - aggregatable: string; - type: string; - missing: boolean; - [index: string]: string | boolean; -} - export interface Props { /** * triggered on input of user into search field From 5540d32a75faf78dae125f50d6cc6f2ead2e5b0b Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Mon, 6 Dec 2021 12:21:57 -0800 Subject: [PATCH 4/8] Remove angular code Signed-off-by: Abbas Hussain --- .../application/components/data_tab/wizard_field_search.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx index 220835d35140..6ea14221738d 100644 --- a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx @@ -54,12 +54,6 @@ export function WizardFieldSearch({ onChange, value }: Props) { defaultMessage: 'Search field names', }); - if (typeof value !== 'string') { - // at initial rendering value is undefined (angular related), this catches the warning - // should be removed once all is react - return null; - } - return ( From ef1c3a9a74d0e7bd94e40f54e850d1638fe833d4 Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Tue, 7 Dec 2021 09:56:41 -0800 Subject: [PATCH 5/8] Refactor Signed-off-by: Abbas Hussain --- .../{wizard_field_search.tsx => field_search.tsx} | 2 +- .../components/data_tab/field_selector.tsx | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) rename src/plugins/wizard/public/application/components/data_tab/{wizard_field_search.tsx => field_search.tsx} (97%) diff --git a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx b/src/plugins/wizard/public/application/components/data_tab/field_search.tsx similarity index 97% rename from src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx rename to src/plugins/wizard/public/application/components/data_tab/field_search.tsx index 6ea14221738d..6b636818ce68 100644 --- a/src/plugins/wizard/public/application/components/data_tab/wizard_field_search.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/field_search.tsx @@ -49,7 +49,7 @@ export interface Props { * Component is Wizard's side bar to search of available fields * Additionally there's a button displayed that allows the user to show/hide more filter fields */ -export function WizardFieldSearch({ onChange, value }: Props) { +export function FieldSearch({ onChange, value }: Props) { const searchPlaceholder = i18n.translate('wizard.fieldChooser.searchPlaceHolder', { defaultMessage: 'Search field names', }); diff --git a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx index 59275b426f9c..d931a33fc2dd 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx @@ -5,7 +5,7 @@ import React, { useCallback, useState, useEffect } from 'react'; import { EuiFlexItem, EuiAccordion, EuiSpacer, EuiNotificationBadge, EuiTitle } from '@elastic/eui'; -import { WizardFieldSearch } from './wizard_field_search'; +import { FieldSearch } from './field_search'; import { IndexPatternField, @@ -36,13 +36,9 @@ export const FieldSelector = () => { const [fieldSearchValue, setFieldSearchValue] = useState(''); useEffect(() => { - const filteredSubset = indexFields.filter((field) => { - if (!field.displayName.includes(fieldSearchValue)) { - return false; - } - - return true; - }); + const filteredSubset = indexFields.filter((field) => + field.displayName.includes(fieldSearchValue) + ); setFilteredFields(filteredSubset); return; @@ -70,7 +66,7 @@ export const FieldSelector = () => {
- +
From cd1f9710deaff8ff0452853e648c116c717bb623 Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Wed, 22 Dec 2021 07:47:28 -0800 Subject: [PATCH 6/8] Add search field value to plugin state Signed-off-by: Abbas Hussain --- .../application/utils/state_management/datasource_slice.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts b/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts index 9bffb56ededa..ebde8f2df3c8 100644 --- a/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts +++ b/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts @@ -13,11 +13,13 @@ const ALLOWED_FIELDS: string[] = [OSD_FIELD_TYPES.STRING, OSD_FIELD_TYPES.NUMBER interface DataSourceState { indexPattern: IndexPattern | null; visualizableFields: IndexPatternField[]; + searchField: string; } const initialState: DataSourceState = { indexPattern: null, visualizableFields: [], + searchField: '', }; export const slice = createSlice({ @@ -35,7 +37,7 @@ export const { reducer } = slice; export const { setIndexPattern } = slice.actions; // TODO: Temporary validate function -// Need to identify hopw to get fieldCounts to use the standard filter and group functions +// Need to identify how to get fieldCounts to use the standard filter and group functions function isVisualizable(field: IndexPatternField): boolean { const isAggregatable = field.aggregatable === true; const isNotScripted = !field.scripted; From 5767d3d79f0b6a53037a24e197a65a299f0a1cd6 Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Mon, 3 Jan 2022 16:10:26 -0800 Subject: [PATCH 7/8] Filter fields based on searchField state Signed-off-by: Abbas Hussain --- .../components/data_tab/field_search.tsx | 13 ++++++------- .../components/data_tab/field_selector.tsx | 8 ++------ .../utils/state_management/datasource_slice.ts | 5 ++++- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/plugins/wizard/public/application/components/data_tab/field_search.tsx b/src/plugins/wizard/public/application/components/data_tab/field_search.tsx index 6b636818ce68..47f399dd2484 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_search.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/field_search.tsx @@ -32,13 +32,10 @@ import React from 'react'; import { i18n } from '@osd/i18n'; import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { setSearchField } from '../../utils/state_management/datasource_slice'; +import { useTypedDispatch } from '../../utils/state_management'; export interface Props { - /** - * triggered on input of user into search field - */ - onChange: (field: string) => void; - /** * the input value of the user */ @@ -49,11 +46,13 @@ export interface Props { * Component is Wizard's side bar to search of available fields * Additionally there's a button displayed that allows the user to show/hide more filter fields */ -export function FieldSearch({ onChange, value }: Props) { +export function FieldSearch({ value }: Props) { const searchPlaceholder = i18n.translate('wizard.fieldChooser.searchPlaceHolder', { defaultMessage: 'Search field names', }); + const dispatch = useTypedDispatch(); + return ( @@ -63,7 +62,7 @@ export function FieldSearch({ onChange, value }: Props) { data-test-subj="fieldFilterSearchInput" compressed fullWidth - onChange={(event) => onChange(event.currentTarget.value)} + onChange={(event) => dispatch(setSearchField(event.currentTarget.value))} placeholder={searchPlaceholder} value={value} /> diff --git a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx index d931a33fc2dd..1464f31aabd9 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/field_selector.tsx @@ -33,7 +33,7 @@ const META_FIELDS: string[] = [ export const FieldSelector = () => { const indexFields = useTypedSelector((state) => state.dataSource.visualizableFields); const [filteredFields, setFilteredFields] = useState(indexFields); - const [fieldSearchValue, setFieldSearchValue] = useState(''); + const fieldSearchValue = useTypedSelector((state) => state.dataSource.searchField); useEffect(() => { const filteredSubset = indexFields.filter((field) => @@ -58,15 +58,11 @@ export const FieldSelector = () => { } ); - const onChangeFieldSearch = useCallback((field: string) => { - setFieldSearchValue(field); - }, []); - return (
- +
diff --git a/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts b/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts index ebde8f2df3c8..a84f1a73ca04 100644 --- a/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts +++ b/src/plugins/wizard/public/application/utils/state_management/datasource_slice.ts @@ -30,11 +30,14 @@ export const slice = createSlice({ state.indexPattern = action.payload; state.visualizableFields = action.payload.fields.filter(isVisualizable); }, + setSearchField: (state, action: PayloadAction) => { + state.searchField = action.payload; + }, }, }); export const { reducer } = slice; -export const { setIndexPattern } = slice.actions; +export const { setIndexPattern, setSearchField } = slice.actions; // TODO: Temporary validate function // Need to identify how to get fieldCounts to use the standard filter and group functions From 4e8a7b2b6481baf8d08b8fd8f0976bb296a12012 Mon Sep 17 00:00:00 2001 From: Abbas Hussain Date: Mon, 3 Jan 2022 16:14:16 -0800 Subject: [PATCH 8/8] Shorten license header Signed-off-by: Abbas Hussain --- .../components/data_tab/field_search.tsx | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/plugins/wizard/public/application/components/data_tab/field_search.tsx b/src/plugins/wizard/public/application/components/data_tab/field_search.tsx index 47f399dd2484..2db8404c93c6 100644 --- a/src/plugins/wizard/public/application/components/data_tab/field_search.tsx +++ b/src/plugins/wizard/public/application/components/data_tab/field_search.tsx @@ -1,34 +1,8 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ import React from 'react'; import { i18n } from '@osd/i18n'; import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';