diff --git a/src/plugins/data/common/constants.ts b/src/plugins/data/common/constants.ts index 863877322ad9..1de7ae5df034 100644 --- a/src/plugins/data/common/constants.ts +++ b/src/plugins/data/common/constants.ts @@ -63,4 +63,5 @@ export const UI_SETTINGS = { QUERY_ENHANCEMENTS_ENABLED: 'query:enhancements:enabled', QUERY_DATAFRAME_HYDRATION_STRATEGY: 'query:dataframe:hydrationStrategy', SEARCH_QUERY_LANGUAGE_BLOCKLIST: 'search:queryLanguageBlocklist', + NEW_HOME_PAGE: 'home:useNewHomePage', } as const; diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss index b3bf36830ab7..e9300b9f6e79 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_group.scss @@ -55,3 +55,12 @@ margin-top: $euiSize * -1; } } + +.globalFilterGroup__removeAllFilters { + color: $euiColorDangerText; +} + +.globalFilterGroup__filterPrefix { + padding-top: 10px; + padding-right: 10px; +} diff --git a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx index 964b1fe82fb7..ec27369abe31 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx @@ -35,6 +35,7 @@ import { EuiFlexItem, EuiPopover, EuiResizeObserver, + EuiText, } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@osd/i18n/react'; import classNames from 'classnames'; @@ -43,20 +44,10 @@ import { stringify } from '@osd/std'; import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; -import { FilterOptions } from './filter_options'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; import { IIndexPattern } from '../..'; -import { - buildEmptyFilter, - Filter, - enableFilter, - disableFilter, - pinFilter, - toggleFilterDisabled, - toggleFilterNegated, - unpinFilter, - UI_SETTINGS, -} from '../../../common'; +import { buildEmptyFilter, Filter, UI_SETTINGS } from '../../../common'; +import { FilterOptions } from './filter_options'; interface Props { filters: Filter[]; @@ -74,6 +65,7 @@ function FilterBarUI(props: Props) { const [filterWidth, setFilterWidth] = useState(maxFilterWidth); const uiSettings = opensearchDashboards.services.uiSettings; + const useNewHeader = Boolean(uiSettings!.get(UI_SETTINGS.NEW_HOME_PAGE)); if (!uiSettings) return null; function onFiltersUpdated(filters: Filter[]) { @@ -177,40 +169,6 @@ function FilterBarUI(props: Props) { onFiltersUpdated(filters); } - function onEnableAll() { - const filters = props.filters.map(enableFilter); - onFiltersUpdated(filters); - } - - function onDisableAll() { - const filters = props.filters.map(disableFilter); - onFiltersUpdated(filters); - } - - function onPinAll() { - const filters = props.filters.map(pinFilter); - onFiltersUpdated(filters); - } - - function onUnpinAll() { - const filters = props.filters.map(unpinFilter); - onFiltersUpdated(filters); - } - - function onToggleAllNegated() { - const filters = props.filters.map(toggleFilterNegated); - onFiltersUpdated(filters); - } - - function onToggleAllDisabled() { - const filters = props.filters.map(toggleFilterDisabled); - onFiltersUpdated(filters); - } - - function onRemoveAll() { - onFiltersUpdated([]); - } - const classes = classNames('globalFilterBar', props.className); return ( @@ -221,15 +179,18 @@ function FilterBarUI(props: Props) { responsive={false} > - + {useNewHeader ? ( + + Filters: + + ) : ( + + )} diff --git a/src/plugins/data/public/ui/filter_bar/filter_options.tsx b/src/plugins/data/public/ui/filter_bar/filter_options.tsx index b61bc1804dc3..c565fd90e44e 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_options.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_options.tsx @@ -29,177 +29,391 @@ */ import { - EuiSmallButtonIcon, EuiContextMenu, EuiPopover, - EuiPopoverTitle, EuiToolTip, + EuiButton, + EuiPopoverFooter, + EuiFlexGroup, + EuiFlexItem, + EuiSmallButtonEmpty, + EuiIcon, + EuiResizeObserver, + EuiContextMenuPanel, } from '@elastic/eui'; -import { FormattedMessage, InjectedIntl, injectI18n } from '@osd/i18n/react'; -import { Component } from 'react'; +import { stringify } from '@osd/std'; +import { InjectedIntl, injectI18n } from '@osd/i18n/react'; +import { i18n } from '@osd/i18n'; import React from 'react'; +import { + buildEmptyFilter, + Filter, + enableFilter, + disableFilter, + pinFilter, + toggleFilterDisabled, + toggleFilterNegated, + unpinFilter, + UI_SETTINGS, + IIndexPattern, +} from '../../../common'; +import { FilterEditor } from './filter_editor'; +import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; +import { SavedQueryManagementComponent } from '../saved_query_management'; +import { SavedQuery, SavedQueryService } from '../../query'; interface Props { - onEnableAll: () => void; - onDisableAll: () => void; - onPinAll: () => void; - onUnpinAll: () => void; - onToggleAllNegated: () => void; - onToggleAllDisabled: () => void; - onRemoveAll: () => void; intl: InjectedIntl; + filters: Filter[]; + indexPatterns: IIndexPattern[]; + savedQueryService: SavedQueryService; + // Show when user has privileges to save + showSaveQuery?: boolean; + onSave: () => void; + onSaveAsNew: () => void; + onLoad: (savedQuery: SavedQuery) => void; + onClearSavedQuery: () => void; + onFiltersUpdated?: (filters: Filter[]) => void; + loadedSavedQuery?: SavedQuery; + useSaveQueryMenu: boolean; } +const maxFilterWidth = 600; -interface State { - isPopoverOpen: boolean; -} +const FilterOptionsUI = (props: Props) => { + const [isPopoverOpen, setIsPopoverOpen] = React.useState(false); + const [renderedComponent, setRenderedComponent] = React.useState('menu'); + const [filterWidth, setFilterWidth] = React.useState(maxFilterWidth); + const [showSaveQueryButton, setShowSaveQueryButton] = React.useState(true); + const opensearchDashboards = useOpenSearchDashboards(); + const uiSettings = opensearchDashboards.services.uiSettings; + const isPinned = uiSettings!.get(UI_SETTINGS.FILTERS_PINNED_BY_DEFAULT); + const useNewHeader = Boolean(uiSettings!.get(UI_SETTINGS.NEW_HOME_PAGE)); + const [indexPattern] = props.indexPatterns; + const index = indexPattern && indexPattern.id; + const newFilter = buildEmptyFilter(isPinned, index); -class FilterOptionsUI extends Component { - public state: State = { - isPopoverOpen: false, + const togglePopover = () => { + setRenderedComponent('menu'); + setShowSaveQueryButton(true); + setIsPopoverOpen((prevState) => !prevState); }; - public togglePopover = () => { - this.setState((prevState) => ({ - isPopoverOpen: !prevState.isPopoverOpen, - })); + const closePopover = () => { + setIsPopoverOpen(false); }; - public closePopover = () => { - this.setState({ isPopoverOpen: false }); + function onFiltersUpdated(filters: Filter[]) { + if (props.onFiltersUpdated) { + props.onFiltersUpdated(filters); + } + } + + function onEnableAll() { + const filters = props.filters.map(enableFilter); + onFiltersUpdated(filters); + } + + function onDisableAll() { + const filters = props.filters.map(disableFilter); + onFiltersUpdated(filters); + } + + function onPinAll() { + const filters = props.filters.map(pinFilter); + onFiltersUpdated(filters); + } + + function onUnpinAll() { + const filters = props.filters.map(unpinFilter); + onFiltersUpdated(filters); + } + + function onToggleAllNegated() { + const filters = props.filters.map(toggleFilterNegated); + onFiltersUpdated(filters); + } + + function onToggleAllDisabled() { + const filters = props.filters.map(toggleFilterDisabled); + onFiltersUpdated(filters); + } + + function onRemoveAll() { + onFiltersUpdated([]); + } + + function onAdd(filter: Filter) { + setIsPopoverOpen(false); + const filters = [...props.filters, filter]; + onFiltersUpdated(filters); + } + + function onResize(dimensions: { height: number; width: number }) { + setFilterWidth(dimensions.width); + } + + const addFilterPanelItem = { + name: props.intl.formatMessage({ + id: 'data.filter.options.addFiltersButtonLabel', + defaultMessage: 'Add filters', + }), + icon: 'plusInCircle', + onClick: () => { + setRenderedComponent('addFilter'); + setShowSaveQueryButton(false); + }, + 'data-test-subj': 'addFilters', + disabled: false, }; - public render() { - const panelTree = { + const disableMenuOption = props.filters.length === 0 && useNewHeader; + + const panelTree = [ + { id: 0, + title: 'Filters', items: [ { - name: this.props.intl.formatMessage({ + name: props.intl.formatMessage({ id: 'data.filter.options.enableAllFiltersButtonLabel', defaultMessage: 'Enable all', }), icon: 'eye', onClick: () => { - this.closePopover(); - this.props.onEnableAll(); + closePopover(); + onEnableAll(); }, 'data-test-subj': 'enableAllFilters', + disabled: disableMenuOption, }, { - name: this.props.intl.formatMessage({ + name: props.intl.formatMessage({ id: 'data.filter.options.disableAllFiltersButtonLabel', defaultMessage: 'Disable all', }), icon: 'eyeClosed', onClick: () => { - this.closePopover(); - this.props.onDisableAll(); + closePopover(); + onDisableAll(); }, 'data-test-subj': 'disableAllFilters', + disabled: disableMenuOption, }, { - name: this.props.intl.formatMessage({ + name: props.intl.formatMessage({ id: 'data.filter.options.pinAllFiltersButtonLabel', defaultMessage: 'Pin all', }), icon: 'pin', onClick: () => { - this.closePopover(); - this.props.onPinAll(); + closePopover(); + onPinAll(); }, 'data-test-subj': 'pinAllFilters', + disabled: disableMenuOption, }, { - name: this.props.intl.formatMessage({ + name: props.intl.formatMessage({ id: 'data.filter.options.unpinAllFiltersButtonLabel', defaultMessage: 'Unpin all', }), icon: 'pin', onClick: () => { - this.closePopover(); - this.props.onUnpinAll(); + closePopover(); + onUnpinAll(); }, 'data-test-subj': 'unpinAllFilters', + disabled: disableMenuOption, }, { - name: this.props.intl.formatMessage({ + name: props.intl.formatMessage({ id: 'data.filter.options.invertNegatedFiltersButtonLabel', defaultMessage: 'Invert inclusion', }), icon: 'invert', onClick: () => { - this.closePopover(); - this.props.onToggleAllNegated(); + closePopover(); + onToggleAllNegated(); }, 'data-test-subj': 'invertInclusionAllFilters', + disabled: disableMenuOption, }, { - name: this.props.intl.formatMessage({ + name: props.intl.formatMessage({ id: 'data.filter.options.invertDisabledFiltersButtonLabel', defaultMessage: 'Invert enabled/disabled', }), icon: 'eye', onClick: () => { - this.closePopover(); - this.props.onToggleAllDisabled(); + closePopover(); + onToggleAllDisabled(); }, 'data-test-subj': 'invertEnableDisableAllFilters', + disabled: disableMenuOption, }, { - name: this.props.intl.formatMessage({ + name: props.intl.formatMessage({ id: 'data.filter.options.deleteAllFiltersButtonLabel', defaultMessage: 'Remove all', }), icon: 'trash', onClick: () => { - this.closePopover(); - this.props.onRemoveAll(); + closePopover(); + onRemoveAll(); }, 'data-test-subj': 'removeAllFilters', + disabled: disableMenuOption, + className: useNewHeader ? 'globalFilterGroup__removeAllFilters' : '', }, ], - }; - - return ( - - - - } - anchorPosition="rightUp" - panelPaddingSize="none" - repositionOnScroll - > - - - - - - ); + }, + ]; + + const handleSave = () => { + if (props.onSave) { + props.onSave(); + } + setIsPopoverOpen(false); + }; + + const saveQueryPanel = ( + { + setIsPopoverOpen(false); + }} + />, + ]} + /> + ); + + const menuPanel = ; + const addFilterPanel = ( + + {(resizeRef) => ( +
+ + setIsPopoverOpen(false)} + key={stringify(newFilter)} + /> + +
+ )} + , + ]} + /> + ); + const renderComponent = () => { + switch (renderedComponent) { + case 'menu': + return menuPanel; + case 'addFilter': + return addFilterPanel; + case 'saveQuery': + return saveQueryPanel; + } + }; + + if (useNewHeader) { + panelTree[0].items.unshift(addFilterPanelItem); } -} + + const label = i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', { + defaultMessage: 'See saved queries', + }); + + const savedQueryPopoverButton = ( + + + + ); + + const filterPopoverButton = ( + + + + {useNewHeader && } + + + ); + + return ( + + {useNewHeader ? renderComponent() : props.useSaveQueryMenu ? saveQueryPanel : menuPanel} + {useNewHeader && showSaveQueryButton && ( + + + + { + setRenderedComponent('saveQuery'); + setShowSaveQueryButton(false); + }} + > + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save query', + })} + + + + + )} + + ); +}; export const FilterOptions = injectI18n(FilterOptionsUI); diff --git a/src/plugins/data/public/ui/saved_query_management/__snapshots__/saved_query_management_component.test.tsx.snap b/src/plugins/data/public/ui/saved_query_management/__snapshots__/saved_query_management_component.test.tsx.snap index 4656759e60de..67e522b745a0 100644 --- a/src/plugins/data/public/ui/saved_query_management/__snapshots__/saved_query_management_component.test.tsx.snap +++ b/src/plugins/data/public/ui/saved_query_management/__snapshots__/saved_query_management_component.test.tsx.snap @@ -1,68 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Saved query management component has a popover button 1`] = ` - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - id="savedQueryPopover" - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - repositionOnScroll={true} -> -
- - Saved Queries - - -

- There are no saved queries. Save query text and filters that you want to use again. -

-
- - - - - - - -
-
-`; +exports[`Saved query management component has a popover button 1`] = `null`; diff --git a/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx index 8504ec858b0d..0ec90f61ae09 100644 --- a/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx +++ b/src/plugins/data/public/ui/saved_query_management/saved_query_management_component.tsx @@ -29,10 +29,8 @@ */ import { - EuiPopover, EuiPopoverTitle, EuiPopoverFooter, - EuiSmallButtonEmpty, EuiButtonEmpty, EuiButton, EuiFlexGroup, @@ -41,7 +39,6 @@ import { EuiPagination, EuiText, EuiSpacer, - EuiIcon, } from '@elastic/eui'; import { i18n } from '@osd/i18n'; @@ -59,6 +56,7 @@ interface Props { onSaveAsNew: () => void; onLoad: (savedQuery: SavedQuery) => void; onClearSavedQuery: () => void; + closeMenuPopover?: () => void; } export function SavedQueryManagementComponent({ @@ -69,8 +67,9 @@ export function SavedQueryManagementComponent({ onLoad, onClearSavedQuery, savedQueryService, + closeMenuPopover, }: Props) { - const [isOpen, setIsOpen] = useState(false); + const [isOpen, setIsOpen] = useState(true); const [savedQueries, setSavedQueries] = useState([] as SavedQuery[]); const [count, setTotalCount] = useState(0); const [activePage, setActivePage] = useState(0); @@ -100,11 +99,12 @@ export function SavedQueryManagementComponent({ } }, [isOpen, activePage, savedQueryService]); - const handleTogglePopover = useCallback(() => setIsOpen((currentState) => !currentState), [ - setIsOpen, - ]); - - const handleClosePopover = useCallback(() => setIsOpen(false), []); + const handleClosePopover = useCallback(() => { + setIsOpen(false); + if (closeMenuPopover) { + closeMenuPopover(); + } + }, [closeMenuPopover]); const handleSave = useCallback(() => { handleClosePopover(); @@ -170,21 +170,6 @@ export function SavedQueryManagementComponent({ const goToPage = (pageNumber: number) => { setActivePage(pageNumber); }; - const label = i18n.translate('data.search.searchBar.savedQueryPopoverButtonText', { - defaultMessage: 'See saved queries', - }); - - const savedQueryPopoverButton = ( - - - - ); const savedQueryRows = () => { const savedQueriesWithoutCurrent = savedQueries.filter((savedQuery) => { @@ -208,150 +193,130 @@ export function SavedQueryManagementComponent({ }; return ( - - -
+ + {savedQueryPopoverTitleText} + + {savedQueries.length > 0 ? ( + + +

{savedQueryDescriptionText}

+
+
+ + {savedQueryRows()} + +
+ +
+ ) : ( + + +

{noSavedQueriesDescriptionText}

+
+ +
+ )} + + - - {savedQueryPopoverTitleText} - - {savedQueries.length > 0 ? ( + {showSaveQuery && loadedSavedQuery && ( - -

{savedQueryDescriptionText}

-
-
- + - {savedQueryRows()} - -
- -
- ) : ( - - -

{noSavedQueriesDescriptionText}

-
- + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveChangesButtonText', { + defaultMessage: 'Save changes', + })} + + + + + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', { + defaultMessage: 'Save as new', + })} + +
)} - - - {showSaveQuery && loadedSavedQuery && ( - - - - {i18n.translate( - 'data.search.searchBar.savedQueryPopoverSaveChangesButtonText', - { - defaultMessage: 'Save changes', - } - )} - - - - - {i18n.translate( - 'data.search.searchBar.savedQueryPopoverSaveAsNewButtonText', - { - defaultMessage: 'Save as new', - } - )} - - - - )} - {showSaveQuery && !loadedSavedQuery && ( - - - {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { - defaultMessage: 'Save current query', - })} - - - )} - - - {loadedSavedQuery && ( - - {i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { - defaultMessage: 'Clear', - })} - + {showSaveQuery && !loadedSavedQuery && ( + + - - -
-
-
+ data-test-subj="saved-query-management-save-button" + > + {i18n.translate('data.search.searchBar.savedQueryPopoverSaveButtonText', { + defaultMessage: 'Save current query', + })} + +
+ )} + + + {loadedSavedQuery && ( + + {i18n.translate('data.search.searchBar.savedQueryPopoverClearButtonText', { + defaultMessage: 'Clear', + })} + + )} + + + + ); } diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx index 3a83af415f59..3db269074015 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -33,7 +33,6 @@ import classNames from 'classnames'; import { compact, get, isEqual } from 'lodash'; import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; -import { DataSource } from '../..'; import { OpenSearchDashboardsReactContextValue, withOpenSearchDashboards, @@ -45,8 +44,8 @@ import { FilterBar } from '../filter_bar/filter_bar'; import { QueryEditorTopRow } from '../query_editor'; import QueryBarTopRow from '../query_string_input/query_bar_top_row'; import { SavedQueryMeta, SaveQueryForm } from '../saved_query_form'; -import { SavedQueryManagementComponent } from '../saved_query_management'; import { Settings } from '../types'; +import { FilterOptions } from '../filter_bar/filter_options'; interface SearchBarInjectedDeps { opensearchDashboards: OpenSearchDashboardsReactContextValue; @@ -123,6 +122,7 @@ class SearchBarUI extends Component { private savedQueryService = this.services.data.query.savedQueries; public filterBarRef: Element | null = null; public filterBarWrapperRef: Element | null = null; + private useNewHeader = Boolean(this.services.uiSettings.get(UI_SETTINGS.NEW_HOME_PAGE)); public static getDerivedStateFromProps(nextProps: SearchBarProps, prevState: State) { if (isEqual(prevState.currentProps, nextProps)) { @@ -233,6 +233,7 @@ class SearchBarUI extends Component { return ( this.props.showFilterBar && this.props.filters && + (!this.useNewHeader || this.props.filters.length > 0) && this.props.indexPatterns && compact(this.props.indexPatterns).length > 0 && (this.props.settings?.getQueryEnhancements(this.state.query?.language!)?.searchBar @@ -412,17 +413,27 @@ class SearchBarUI extends Component { ); this.props.settings?.setUserQueryEnhancementsEnabled(isEnhancementsEnabledOverride); - const savedQueryManagement = this.state.query && this.props.onClearSavedQuery && ( - - ); + const searchBarMenu = (useSaveQueryMenu: boolean = false) => { + return ( + this.state.query && + this.props.onClearSavedQuery && ( + + ) + ); + }; let filterBar; if (this.shouldRenderFilterBar()) { @@ -464,7 +475,7 @@ class SearchBarUI extends Component { onSubmit={this.onQueryBarSubmit} indexPatterns={this.props.indexPatterns} isLoading={this.props.isLoading} - prepend={this.props.showFilterBar ? savedQueryManagement : undefined} + prepend={this.props.showFilterBar ? searchBarMenu(!this.useNewHeader) : undefined} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} dateRangeTo={this.state.dateRangeTo} @@ -498,7 +509,7 @@ class SearchBarUI extends Component { onSubmit={this.onQueryBarSubmit} indexPatterns={this.props.indexPatterns} isLoading={this.props.isLoading} - prepend={this.props.showFilterBar ? savedQueryManagement : undefined} + prepend={this.props.showFilterBar ? searchBarMenu(!this.useNewHeader) : undefined} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} dateRangeTo={this.state.dateRangeTo}