From ca8e7030352223694bf0fd465230c315da6e64ad Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Wed, 10 Jul 2024 23:27:23 +0000 Subject: [PATCH 01/11] changes Signed-off-by: abbyhu2000 --- src/plugins/data/public/ui/query_editor/query_editor.tsx | 6 ++++++ .../query_editor_extensions/query_editor_extension.tsx | 4 ++++ .../query_editor_extensions/query_editor_extensions.tsx | 2 ++ 3 files changed, 12 insertions(+) diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index 19c4c527038c..be94732cf492 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -289,6 +289,12 @@ export default class QueryEditorUI extends Component { const headerClassName = classNames('osdQueryEditorHeader', this.props.headerClassName); const bannerClassName = classNames('osdQueryEditorBanner', this.props.bannerClassName); + console.log('this.state.isDataSourcesVisible', this.state.isDataSourcesVisible); + console.log('this.state.isDataSetsVisible', this.state.isDataSetsVisible); + + console.log('this.props.dataSourceContainerRef', this.props.dataSourceContainerRef); + console.log('this.props.containerRef', this.props.containerRef); + return (
diff --git a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx index f684aebea1d9..60f6a2f1f9ee 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx @@ -99,6 +99,10 @@ export const QueryEditorExtension: React.FC = (props) return () => subscription.unsubscribe(); }, [props.dependencies, props.config]); + console.log('isEnabled', isEnabled); + console.log('props.config', props.config); + console.log('banner', props.bannerContainer, banner); + console.log('component', props.componentContainer, component); if (!isEnabled) return null; return ( diff --git a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx index 45b0d076877e..1c0c9203e989 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx @@ -24,6 +24,8 @@ const QueryEditorExtensions: React.FC = React.memo(( return Object.values(configMap).sort((a, b) => a.order - b.order); }, [configMap]); + console.log('sortedConfigs', sortedConfigs); + return ( <> {sortedConfigs.map((config) => ( From 66e706c1222f50bba594e0d8e75928ae42c4db8d Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Thu, 11 Jul 2024 00:16:14 +0000 Subject: [PATCH 02/11] Add toggle and single line query editor for DQL/lucene Signed-off-by: abbyhu2000 --- .../ui/query_editor/collapsed_query_bar.tsx | 55 ++++++++++++++++ .../public/ui/query_editor/query_editor.tsx | 62 +++++++++++++------ .../query_editor_btn_collapse.tsx | 37 +++++++++++ .../ui/query_editor/query_editor_top_row.tsx | 3 + 4 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 src/plugins/data/public/ui/query_editor/collapsed_query_bar.tsx create mode 100644 src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx diff --git a/src/plugins/data/public/ui/query_editor/collapsed_query_bar.tsx b/src/plugins/data/public/ui/query_editor/collapsed_query_bar.tsx new file mode 100644 index 000000000000..1d0622f3dc38 --- /dev/null +++ b/src/plugins/data/public/ui/query_editor/collapsed_query_bar.tsx @@ -0,0 +1,55 @@ +/* + * 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. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ +import React, { RefObject, createRef, useState } from 'react'; +import { i18n } from '@osd/i18n'; + +import { EuiFieldText, EuiOutsideClickDetector, EuiPortal, Query } from '@elastic/eui'; +import { SuggestionsComponent } from '../typeahead'; + +export interface CollapsedQueryBarInputProps { + initialValue: string; + onChange?: (query: string) => void; +} + +export const CollapsedQueryBarInput: React.FC = (props) => { + const [isSuggestionsVisible, setIsSuggestionsVisible] = useState(false); + const [suggestionIndex, setSuggestionIndex] = useState(null); + const [value, setValue] = useState(props.initialValue ?? ''); + + return ( + setIsSuggestionsVisible(false)}> +
+ setIsSuggestionsVisible(true)} + onChange={(e) => setValue(e.target.value)} + onKeyDown={() => setIsSuggestionsVisible(true)} + placeholder={''} + fullWidth + /> + + { + return; + }} + onMouseEnter={(i) => setSuggestionIndex(i)} + loadMore={() => {}} + size="s" + /> + +
+
+ ); +}; diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index be94732cf492..906f2c2f05a4 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -20,6 +20,8 @@ import { DataSettings } from '../types'; import { fetchIndexPatterns } from './fetch_index_patterns'; import { QueryLanguageSelector } from './language_selector'; import { QueryEditorExtensions } from './query_editor_extensions'; +import { QueryEditorBtnCollapse } from './query_editor_btn_collapse'; +import { CollapsedQueryBarInput } from './collapsed_query_bar'; export interface QueryEditorProps { indexPatterns: Array; @@ -47,6 +49,8 @@ export interface QueryEditorProps { queryLanguage?: string; headerClassName?: string; bannerClassName?: string; + isCollapsed: boolean; + onToggleCollapsed: () => void; } interface Props extends QueryEditorProps { @@ -289,6 +293,11 @@ export default class QueryEditorUI extends Component { const headerClassName = classNames('osdQueryEditorHeader', this.props.headerClassName); const bannerClassName = classNames('osdQueryEditorBanner', this.props.bannerClassName); + const useQueryEditor = + this.props.query.language === 'SQLAsync' || + this.props.query.language === 'SQL' || + this.props.query.language === 'PPL'; + console.log('this.state.isDataSourcesVisible', this.state.isDataSourcesVisible); console.log('this.state.isDataSetsVisible', this.state.isDataSetsVisible); @@ -301,6 +310,12 @@ export default class QueryEditorUI extends Component { + + + {this.props.prepend} {this.state.isDataSourcesVisible && ( @@ -320,28 +335,39 @@ export default class QueryEditorUI extends Component {
)} + {(!this.props.isCollapsed || !useQueryEditor) && ( + + + + )} +
- + {this.props.isCollapsed && useQueryEditor && ( + + )} {this.renderQueryEditorExtensions()} diff --git a/src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx b/src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx new file mode 100644 index 000000000000..e1c515f87818 --- /dev/null +++ b/src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx @@ -0,0 +1,37 @@ +/* + * 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. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; + +export interface Props { + onClick: () => void; + isCollapsed: boolean; +} + +export function QueryEditorBtnCollapse({ onClick, isCollapsed }: Props) { + const label = i18n.translate('queryEditor.collapse', { + defaultMessage: 'Toggle query editor', + }); + return ( + + onClick()} + iconType={!isCollapsed ? 'arrowRight' : 'arrowDown'} + iconSize={'s'} + /> + + ); +} diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index 3ea715418ab1..56eb62e4c6a1 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -71,6 +71,7 @@ export interface QueryEditorTopRowProps { export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { const [isDateRangeInvalid, setIsDateRangeInvalid] = useState(false); const [isQueryEditorFocused, setIsQueryEditorFocused] = useState(false); + const [isQueryEditorCollapsed, setIsQueryEditorCollapsed] = useState(false); const opensearchDashboards = useOpenSearchDashboards(); const { uiSettings, storage, appName } = opensearchDashboards.services; @@ -247,6 +248,8 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { className="osdQueryEditor" dataTestSubj={props.dataTestSubj} queryLanguage={queryLanguage} + isCollapsed={isQueryEditorCollapsed} + onToggleCollapsed={() => setIsQueryEditorCollapsed(!isQueryEditorCollapsed)} /> ); From 384693dd028cbad553f9237104233cdfa17824ab Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Thu, 11 Jul 2024 00:25:33 +0000 Subject: [PATCH 03/11] add conditional render on filter bar Signed-off-by: abbyhu2000 --- .../public/ui/query_editor/query_editor_top_row.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index 56eb62e4c6a1..fe6893a6c002 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -380,10 +380,15 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { > {renderQueryEditor()} - - {props.filterBar} - {renderSharingMetaFields()} - {renderUpdateButton()} + + {isQueryEditorCollapsed && {props.filterBar}} + + + + {renderSharingMetaFields()} + {renderUpdateButton()} + + From 63414b3ae745691c8fe6e99903426ce5755ec9b7 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Thu, 11 Jul 2024 19:59:33 +0000 Subject: [PATCH 04/11] Add footer into query extension service Signed-off-by: abbyhu2000 --- .../public/ui/query_editor/query_editor.tsx | 58 ++++++++++++------- .../query_editor_extension.tsx | 13 ++++- .../query_editor_extensions.tsx | 10 +++- .../ui/query_editor/query_editor_top_row.tsx | 2 + .../ui/search_bar/create_search_bar.tsx | 9 +++ .../data/public/ui/search_bar/search_bar.tsx | 2 + src/plugins/data/public/ui/types.ts | 1 + src/plugins/data/public/ui/ui_service.ts | 7 +++ 8 files changed, 78 insertions(+), 24 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index 906f2c2f05a4..c6e008074762 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -28,6 +28,7 @@ export interface QueryEditorProps { dataSource?: DataSource; query: Query; dataSourceContainerRef?: React.RefCallback; + dataSourceFooterRef?: React.RefCallback; containerRef?: React.RefCallback; settings: Settings; disableAutoFocus?: boolean; @@ -49,6 +50,7 @@ export interface QueryEditorProps { queryLanguage?: string; headerClassName?: string; bannerClassName?: string; + footerClassName?: string; isCollapsed: boolean; onToggleCollapsed: () => void; } @@ -99,6 +101,7 @@ export default class QueryEditorUI extends Component { private componentIsUnmounting = false; private headerRef: RefObject = createRef(); private bannerRef: RefObject = createRef(); + private footerRef: RefObject = createRef(); private extensionMap = this.props.settings?.getQueryEditorExtensionMap(); private getQueryString = () => { @@ -129,10 +132,15 @@ export default class QueryEditorUI extends Component { }; private renderQueryEditorExtensions() { + console.log('i am here!', this.headerRef.current); + console.log('i am here!', this.bannerRef.current); + console.log('i am here!', this.footerRef.current); + if ( !( this.headerRef.current && this.bannerRef.current && + this.footerRef.current && this.props.queryLanguage && this.extensionMap && Object.keys(this.extensionMap).length > 0 @@ -146,6 +154,7 @@ export default class QueryEditorUI extends Component { configMap={this.extensionMap} componentContainer={this.headerRef.current} bannerContainer={this.bannerRef.current} + footerContainer={this.footerRef.current} indexPatterns={this.props.indexPatterns} dataSource={this.props.dataSource} /> @@ -292,17 +301,18 @@ export default class QueryEditorUI extends Component { const className = classNames(this.props.className); const headerClassName = classNames('osdQueryEditorHeader', this.props.headerClassName); const bannerClassName = classNames('osdQueryEditorBanner', this.props.bannerClassName); + const footerClassName = classNames('osdQueryEditorFooter', this.props.footerClassName); const useQueryEditor = this.props.query.language === 'SQLAsync' || this.props.query.language === 'SQL' || this.props.query.language === 'PPL'; - console.log('this.state.isDataSourcesVisible', this.state.isDataSourcesVisible); - console.log('this.state.isDataSetsVisible', this.state.isDataSetsVisible); + // console.log('this.state.isDataSourcesVisible', this.state.isDataSourcesVisible); + // console.log('this.state.isDataSetsVisible', this.state.isDataSetsVisible); - console.log('this.props.dataSourceContainerRef', this.props.dataSourceContainerRef); - console.log('this.props.containerRef', this.props.containerRef); + // console.log('this.props.dataSourceContainerRef', this.props.dataSourceContainerRef); + // console.log('this.props.containerRef', this.props.containerRef); return (
@@ -349,25 +359,29 @@ export default class QueryEditorUI extends Component {
{this.props.isCollapsed && useQueryEditor && ( - + <> + +
+ )} +
{this.renderQueryEditorExtensions()} diff --git a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx index 60f6a2f1f9ee..b5d93dab1e74 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx @@ -15,6 +15,7 @@ interface QueryEditorExtensionProps { dependencies: QueryEditorExtensionDependencies; componentContainer: Element; bannerContainer: Element; + footerContainer: Element; } export interface QueryEditorExtensionDependencies { @@ -60,8 +61,9 @@ export interface QueryEditorExtensionConfig { * @returns The component the query editor extension. */ getBanner?: (dependencies: QueryEditorExtensionDependencies) => React.ReactElement | null; -} + getFooter?: (dependencies: QueryEditorExtensionDependencies) => React.ReactElement | null; +} const QueryEditorExtensionPortal: React.FC<{ container: Element }> = (props) => { if (!props.children) return null; @@ -85,6 +87,11 @@ export const QueryEditorExtension: React.FC = (props) props.dependencies, ]); + const footer = useMemo(() => props.config.getFooter?.(props.dependencies), [ + props.config, + props.dependencies, + ]); + useEffect(() => { isMounted.current = true; return () => { @@ -103,6 +110,7 @@ export const QueryEditorExtension: React.FC = (props) console.log('props.config', props.config); console.log('banner', props.bannerContainer, banner); console.log('component', props.componentContainer, component); + console.log('footer', props.footerContainer, component); if (!isEnabled) return null; return ( @@ -113,6 +121,9 @@ export const QueryEditorExtension: React.FC = (props) {component} + + {footer} + ); }; diff --git a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx index 1c0c9203e989..63da4432d402 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx @@ -14,10 +14,17 @@ interface QueryEditorExtensionsProps extends QueryEditorExtensionDependencies { configMap?: Record; componentContainer: Element; bannerContainer: Element; + footerContainer: Element; } const QueryEditorExtensions: React.FC = React.memo((props) => { - const { configMap, componentContainer, bannerContainer, ...dependencies } = props; + const { + configMap, + componentContainer, + bannerContainer, + footerContainer, + ...dependencies + } = props; const sortedConfigs = useMemo(() => { if (!configMap || Object.keys(configMap).length === 0) return []; @@ -35,6 +42,7 @@ const QueryEditorExtensions: React.FC = React.memo(( dependencies={dependencies} componentContainer={componentContainer} bannerContainer={bannerContainer} + footerContainer={footerContainer} /> ))} diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index fe6893a6c002..e7b776504006 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -39,6 +39,7 @@ const QueryEditor = withOpenSearchDashboards(QueryEditorUI); export interface QueryEditorTopRowProps { query?: Query; dataSourceContainerRef?: React.RefCallback; + dataSourceFooterRef?: React.RefCallback; containerRef?: React.RefCallback; settings?: Settings; onSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; @@ -237,6 +238,7 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { prepend={props.prepend} query={parsedQuery} dataSourceContainerRef={props.dataSourceContainerRef} + dataSourceFooterRef={props.dataSourceFooterRef} containerRef={props.containerRef} settings={props.settings!} screenTitle={props.screenTitle} diff --git a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx index 244f4296216c..eaa28065fe07 100644 --- a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx @@ -49,6 +49,7 @@ interface StatefulSearchBarDeps { storage: IStorageWrapper; settings: Settings; setDataSourceContainerRef: (ref: HTMLDivElement | null) => void; + setDataSourceFooterRef: (ref: HTMLDivElement | null) => void; setContainerRef: (ref: HTMLDivElement | null) => void; } @@ -140,6 +141,7 @@ export function createSearchBar({ data, settings, setDataSourceContainerRef, + setDataSourceFooterRef, setContainerRef, }: StatefulSearchBarDeps) { // App name should come from the core application service. @@ -182,6 +184,12 @@ export function createSearchBar({ } }, []); + const dataSourceFooterRef = useCallback((node) => { + if (node) { + setDataSourceFooterRef(node); + } + }, []); + const containerRef = useCallback((node) => { if (node) { setContainerRef(node); @@ -229,6 +237,7 @@ export function createSearchBar({ query={query} settings={settings} dataSourceContainerRef={dataSourceContainerRef} + dataSourceFooterRef={dataSourceFooterRef} containerRef={containerRef} onFiltersUpdated={defaultFiltersUpdated(data.query)} onRefreshChange={defaultOnRefreshChange(data.query)} 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 11914f134443..7f9ab80124f3 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -81,6 +81,7 @@ export interface SearchBarOwnProps { query?: Query; settings?: Settings; dataSourceContainerRef?: React.RefCallback; + dataSourceFooterRef?: React.RefCallback; containerRef?: React.RefCallback; // Show when user has privileges to save showSaveQuery?: boolean; @@ -492,6 +493,7 @@ class SearchBarUI extends Component { ; Settings: Settings; dataSourceContainer$: Observable; + dataSourceFooter$: Observable; container$: Observable; } diff --git a/src/plugins/data/public/ui/ui_service.ts b/src/plugins/data/public/ui/ui_service.ts index 243490dc8201..ea5e8b640d5a 100644 --- a/src/plugins/data/public/ui/ui_service.ts +++ b/src/plugins/data/public/ui/ui_service.ts @@ -30,6 +30,7 @@ export class UiService implements Plugin { private queryEnhancements: Map = new Map(); private queryEditorExtensionMap: Record = {}; private dataSourceContainer$ = new BehaviorSubject(null); + private dataSourceFooter$ = new BehaviorSubject(null); private container$ = new BehaviorSubject(null); constructor(initializerContext: PluginInitializerContext) { @@ -67,6 +68,10 @@ export class UiService implements Plugin { this.dataSourceContainer$.next(ref); }; + const setDataSourceFooterRef = (ref: HTMLDivElement | null) => { + this.dataSourceFooter$.next(ref); + }; + const setContainerRef = (ref: HTMLDivElement | null) => { this.container$.next(ref); }; @@ -77,6 +82,7 @@ export class UiService implements Plugin { storage, settings: Settings, setDataSourceContainerRef, + setDataSourceFooterRef, setContainerRef, }); @@ -86,6 +92,7 @@ export class UiService implements Plugin { SuggestionsComponent, Settings, dataSourceContainer$: this.dataSourceContainer$, + dataSourceFooter$: this.dataSourceFooter$, container$: this.container$, }; } From bd7fcc57f88eb0f82628a7c372212e58478f6e5b Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Thu, 11 Jul 2024 22:13:59 +0000 Subject: [PATCH 05/11] move toggle state into query editor state, remove data source footer ref Signed-off-by: abbyhu2000 --- .../public/ui/query_editor/query_editor.tsx | 20 ++++++++++--------- .../query_editor_btn_collapse.tsx | 2 +- .../ui/query_editor/query_editor_top_row.tsx | 16 +++------------ .../ui/search_bar/create_search_bar.tsx | 9 --------- .../data/public/ui/search_bar/search_bar.tsx | 2 -- src/plugins/data/public/ui/ui_service.ts | 5 ----- 6 files changed, 15 insertions(+), 39 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index c6e008074762..25fd76218689 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -28,7 +28,6 @@ export interface QueryEditorProps { dataSource?: DataSource; query: Query; dataSourceContainerRef?: React.RefCallback; - dataSourceFooterRef?: React.RefCallback; containerRef?: React.RefCallback; settings: Settings; disableAutoFocus?: boolean; @@ -51,8 +50,7 @@ export interface QueryEditorProps { headerClassName?: string; bannerClassName?: string; footerClassName?: string; - isCollapsed: boolean; - onToggleCollapsed: () => void; + filterBar?: any; } interface Props extends QueryEditorProps { @@ -66,6 +64,7 @@ interface State { index: number | null; suggestions: QuerySuggestion[]; indexPatterns: IIndexPattern[]; + isCollapsed: boolean; } const KEY_CODES = { @@ -91,6 +90,7 @@ export default class QueryEditorUI extends Component { index: null, suggestions: [], indexPatterns: [], + isCollapsed: true, }; public inputRef: HTMLElement | null = null; @@ -322,8 +322,8 @@ export default class QueryEditorUI extends Component { this.setState({ isCollapsed: !this.state.isCollapsed })} + isCollapsed={this.state.isCollapsed} /> {this.props.prepend} @@ -345,7 +345,7 @@ export default class QueryEditorUI extends Component {
)} - {(!this.props.isCollapsed || !useQueryEditor) && ( + {(!this.state.isCollapsed || !useQueryEditor) && ( {
- {this.props.isCollapsed && useQueryEditor && ( + {this.state.isCollapsed && useQueryEditor && ( <> { wrappingIndent: 'indent', }} /> -
)} -
+ {this.state.isCollapsed && useQueryEditor && ( +
+ )} + {this.state.isCollapsed && {this.props.filterBar}} {this.renderQueryEditorExtensions()}
diff --git a/src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx b/src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx index e1c515f87818..b61ed566b08a 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_btn_collapse.tsx @@ -28,7 +28,7 @@ export function QueryEditorBtnCollapse({ onClick, isCollapsed }: Props) { aria-expanded={!isCollapsed} aria-label={label} data-test-subj="queryEditorCollapseBtn" - onClick={() => onClick()} + onClick={onClick} iconType={!isCollapsed ? 'arrowRight' : 'arrowDown'} iconSize={'s'} /> diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index e7b776504006..416460499aa6 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -39,7 +39,6 @@ const QueryEditor = withOpenSearchDashboards(QueryEditorUI); export interface QueryEditorTopRowProps { query?: Query; dataSourceContainerRef?: React.RefCallback; - dataSourceFooterRef?: React.RefCallback; containerRef?: React.RefCallback; settings?: Settings; onSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; @@ -72,7 +71,6 @@ export interface QueryEditorTopRowProps { export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { const [isDateRangeInvalid, setIsDateRangeInvalid] = useState(false); const [isQueryEditorFocused, setIsQueryEditorFocused] = useState(false); - const [isQueryEditorCollapsed, setIsQueryEditorCollapsed] = useState(false); const opensearchDashboards = useOpenSearchDashboards(); const { uiSettings, storage, appName } = opensearchDashboards.services; @@ -238,7 +236,6 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { prepend={props.prepend} query={parsedQuery} dataSourceContainerRef={props.dataSourceContainerRef} - dataSourceFooterRef={props.dataSourceFooterRef} containerRef={props.containerRef} settings={props.settings!} screenTitle={props.screenTitle} @@ -250,8 +247,7 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { className="osdQueryEditor" dataTestSubj={props.dataTestSubj} queryLanguage={queryLanguage} - isCollapsed={isQueryEditorCollapsed} - onToggleCollapsed={() => setIsQueryEditorCollapsed(!isQueryEditorCollapsed)} + filterBar={props.filterBar} /> ); @@ -383,14 +379,8 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { {renderQueryEditor()} - {isQueryEditorCollapsed && {props.filterBar}} - - - - {renderSharingMetaFields()} - {renderUpdateButton()} - - + {renderSharingMetaFields()} + {renderUpdateButton()} diff --git a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx index eaa28065fe07..244f4296216c 100644 --- a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx @@ -49,7 +49,6 @@ interface StatefulSearchBarDeps { storage: IStorageWrapper; settings: Settings; setDataSourceContainerRef: (ref: HTMLDivElement | null) => void; - setDataSourceFooterRef: (ref: HTMLDivElement | null) => void; setContainerRef: (ref: HTMLDivElement | null) => void; } @@ -141,7 +140,6 @@ export function createSearchBar({ data, settings, setDataSourceContainerRef, - setDataSourceFooterRef, setContainerRef, }: StatefulSearchBarDeps) { // App name should come from the core application service. @@ -184,12 +182,6 @@ export function createSearchBar({ } }, []); - const dataSourceFooterRef = useCallback((node) => { - if (node) { - setDataSourceFooterRef(node); - } - }, []); - const containerRef = useCallback((node) => { if (node) { setContainerRef(node); @@ -237,7 +229,6 @@ export function createSearchBar({ query={query} settings={settings} dataSourceContainerRef={dataSourceContainerRef} - dataSourceFooterRef={dataSourceFooterRef} containerRef={containerRef} onFiltersUpdated={defaultFiltersUpdated(data.query)} onRefreshChange={defaultOnRefreshChange(data.query)} 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 7f9ab80124f3..11914f134443 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -81,7 +81,6 @@ export interface SearchBarOwnProps { query?: Query; settings?: Settings; dataSourceContainerRef?: React.RefCallback; - dataSourceFooterRef?: React.RefCallback; containerRef?: React.RefCallback; // Show when user has privileges to save showSaveQuery?: boolean; @@ -493,7 +492,6 @@ class SearchBarUI extends Component { { this.dataSourceContainer$.next(ref); }; - const setDataSourceFooterRef = (ref: HTMLDivElement | null) => { - this.dataSourceFooter$.next(ref); - }; - const setContainerRef = (ref: HTMLDivElement | null) => { this.container$.next(ref); }; @@ -82,7 +78,6 @@ export class UiService implements Plugin { storage, settings: Settings, setDataSourceContainerRef, - setDataSourceFooterRef, setContainerRef, }); From d33c62e8c0cd848bc39a7efd518baa25babf1535 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Thu, 11 Jul 2024 22:46:07 +0000 Subject: [PATCH 06/11] attempt to use display:none to hide footer Signed-off-by: abbyhu2000 --- .../public/ui/query_editor/_query_editor.scss | 4 ++ .../public/ui/query_editor/query_editor.tsx | 46 +++++++++---------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/_query_editor.scss b/src/plugins/data/public/ui/query_editor/_query_editor.scss index 80ddd42fede5..a5582d278d50 100644 --- a/src/plugins/data/public/ui/query_editor/_query_editor.scss +++ b/src/plugins/data/public/ui/query_editor/_query_editor.scss @@ -15,6 +15,10 @@ // overflow: auto; } +.osdQueryEditorFooterHide { + display: "none" +} + .osdQueryEditor__languageWrapper { :first-child { box-shadow: none !important; diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index 25fd76218689..8d55b873372f 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -359,30 +359,30 @@ export default class QueryEditorUI extends Component {
{this.state.isCollapsed && useQueryEditor && ( - <> - - - )} - {this.state.isCollapsed && useQueryEditor && ( -
+ )} + +
{this.state.isCollapsed && {this.props.filterBar}} From 3f406e9a0900e7bbd9d78d5ed3a53956d4d5db47 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Mon, 15 Jul 2024 21:52:48 +0000 Subject: [PATCH 07/11] new changes Signed-off-by: abbyhu2000 --- .../public/ui/query_editor/_query_editor.scss | 2 +- .../components/query_editor_body.tsx | 89 ++++++++++++++++++ .../components/query_editor_footer.tsx | 91 +++++++++++++++++++ .../components/query_editor_header.tsx | 90 ++++++++++++++++++ .../public/ui/query_editor/query_editor.tsx | 1 + .../ui/search_bar/create_search_bar.tsx | 1 + .../data/public/ui/search_bar/search_bar.tsx | 1 + 7 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/plugins/data/public/ui/query_editor/components/query_editor_body.tsx create mode 100644 src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx create mode 100644 src/plugins/data/public/ui/query_editor/components/query_editor_header.tsx diff --git a/src/plugins/data/public/ui/query_editor/_query_editor.scss b/src/plugins/data/public/ui/query_editor/_query_editor.scss index a5582d278d50..314d38334d8a 100644 --- a/src/plugins/data/public/ui/query_editor/_query_editor.scss +++ b/src/plugins/data/public/ui/query_editor/_query_editor.scss @@ -16,7 +16,7 @@ } .osdQueryEditorFooterHide { - display: "none" + display: none } .osdQueryEditor__languageWrapper { diff --git a/src/plugins/data/public/ui/query_editor/components/query_editor_body.tsx b/src/plugins/data/public/ui/query_editor/components/query_editor_body.tsx new file mode 100644 index 000000000000..efb80834e0f4 --- /dev/null +++ b/src/plugins/data/public/ui/query_editor/components/query_editor_body.tsx @@ -0,0 +1,89 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiComboBox, EuiComboBoxOptionOption, PopoverAnchorPosition } from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import React from 'react'; +import { getUiService } from '../../services'; + +interface Props { + language: string; + onSelectLanguage: (newLanguage: string) => void; + anchorPosition?: PopoverAnchorPosition; + appName?: string; +} + +const mapExternalLanguageToOptions = (language: string) => { + return { + label: language, + value: language, + }; +}; + +// code editor(PPL or SQL), or nothing(DQL or Lucene), filter bar +export const QueryEditorBody = (props: Props) => { + const dqlLabel = i18n.translate('data.query.queryEditor.dqlLanguageName', { + defaultMessage: 'DQL', + }); + const luceneLabel = i18n.translate('data.query.queryEditor.luceneLanguageName', { + defaultMessage: 'Lucene', + }); + + const languageOptions: EuiComboBoxOptionOption[] = [ + { + label: dqlLabel, + value: 'kuery', + }, + { + label: luceneLabel, + value: 'lucene', + }, + ]; + + const uiService = getUiService(); + + const queryEnhancements = uiService.Settings.getAllQueryEnhancements(); + queryEnhancements.forEach((enhancement) => { + if ( + (enhancement.supportedAppNames && + props.appName && + !enhancement.supportedAppNames.includes(props.appName)) || + uiService.Settings.getUserQueryLanguageBlocklist().includes( + enhancement.language.toLowerCase() + ) + ) + return; + languageOptions.unshift(mapExternalLanguageToOptions(enhancement.language)); + }); + + const selectedLanguage = { + label: + (languageOptions.find( + (option) => (option.value as string).toLowerCase() === props.language.toLowerCase() + )?.label as string) ?? languageOptions[0].label, + }; + + const handleLanguageChange = (newLanguage: EuiComboBoxOptionOption[]) => { + const queryLanguage = newLanguage[0].value as string; + props.onSelectLanguage(queryLanguage); + uiService.Settings.setUserQueryLanguage(queryLanguage); + }; + + uiService.Settings.setUserQueryLanguage(props.language); + + return ( + + ); +}; diff --git a/src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx b/src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx new file mode 100644 index 000000000000..80480c3300dd --- /dev/null +++ b/src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx @@ -0,0 +1,91 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiComboBox, EuiComboBoxOptionOption, PopoverAnchorPosition } from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import React from 'react'; +import { getUiService } from '../../services'; + +interface Props { + language: string; + onSelectLanguage: (newLanguage: string) => void; + anchorPosition?: PopoverAnchorPosition; + appName?: string; +} + +const mapExternalLanguageToOptions = (language: string) => { + return { + label: language, + value: language, + }; +}; + +// footer container ref: language selector, line count, timestamp fields, +// errors, feedbacks(ref from query enhancement plugin), shortcuts +// all the above are registerable by language +export const QueryEditorFooter = (props: Props) => { + const dqlLabel = i18n.translate('data.query.queryEditor.dqlLanguageName', { + defaultMessage: 'DQL', + }); + const luceneLabel = i18n.translate('data.query.queryEditor.luceneLanguageName', { + defaultMessage: 'Lucene', + }); + + const languageOptions: EuiComboBoxOptionOption[] = [ + { + label: dqlLabel, + value: 'kuery', + }, + { + label: luceneLabel, + value: 'lucene', + }, + ]; + + const uiService = getUiService(); + + const queryEnhancements = uiService.Settings.getAllQueryEnhancements(); + queryEnhancements.forEach((enhancement) => { + if ( + (enhancement.supportedAppNames && + props.appName && + !enhancement.supportedAppNames.includes(props.appName)) || + uiService.Settings.getUserQueryLanguageBlocklist().includes( + enhancement.language.toLowerCase() + ) + ) + return; + languageOptions.unshift(mapExternalLanguageToOptions(enhancement.language)); + }); + + const selectedLanguage = { + label: + (languageOptions.find( + (option) => (option.value as string).toLowerCase() === props.language.toLowerCase() + )?.label as string) ?? languageOptions[0].label, + }; + + const handleLanguageChange = (newLanguage: EuiComboBoxOptionOption[]) => { + const queryLanguage = newLanguage[0].value as string; + props.onSelectLanguage(queryLanguage); + uiService.Settings.setUserQueryLanguage(queryLanguage); + }; + + uiService.Settings.setUserQueryLanguage(props.language); + + return ( + + ); +}; diff --git a/src/plugins/data/public/ui/query_editor/components/query_editor_header.tsx b/src/plugins/data/public/ui/query_editor/components/query_editor_header.tsx new file mode 100644 index 000000000000..86a0b38e4f0a --- /dev/null +++ b/src/plugins/data/public/ui/query_editor/components/query_editor_header.tsx @@ -0,0 +1,90 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiComboBox, EuiComboBoxOptionOption, PopoverAnchorPosition } from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import React from 'react'; +import { getUiService } from '../../services'; + +interface Props { + language: string; + onSelectLanguage: (newLanguage: string) => void; + anchorPosition?: PopoverAnchorPosition; + appName?: string; +} + +const mapExternalLanguageToOptions = (language: string) => { + return { + label: language, + value: language, + }; +}; + +// This is the expansion button, and containerRef(dataset selector), +// language actions, query actions +export const QueryEditorHeader = (props: Props) => { + const dqlLabel = i18n.translate('data.query.queryEditor.dqlLanguageName', { + defaultMessage: 'DQL', + }); + const luceneLabel = i18n.translate('data.query.queryEditor.luceneLanguageName', { + defaultMessage: 'Lucene', + }); + + const languageOptions: EuiComboBoxOptionOption[] = [ + { + label: dqlLabel, + value: 'kuery', + }, + { + label: luceneLabel, + value: 'lucene', + }, + ]; + + const uiService = getUiService(); + + const queryEnhancements = uiService.Settings.getAllQueryEnhancements(); + queryEnhancements.forEach((enhancement) => { + if ( + (enhancement.supportedAppNames && + props.appName && + !enhancement.supportedAppNames.includes(props.appName)) || + uiService.Settings.getUserQueryLanguageBlocklist().includes( + enhancement.language.toLowerCase() + ) + ) + return; + languageOptions.unshift(mapExternalLanguageToOptions(enhancement.language)); + }); + + const selectedLanguage = { + label: + (languageOptions.find( + (option) => (option.value as string).toLowerCase() === props.language.toLowerCase() + )?.label as string) ?? languageOptions[0].label, + }; + + const handleLanguageChange = (newLanguage: EuiComboBoxOptionOption[]) => { + const queryLanguage = newLanguage[0].value as string; + props.onSelectLanguage(queryLanguage); + uiService.Settings.setUserQueryLanguage(queryLanguage); + }; + + uiService.Settings.setUserQueryLanguage(props.language); + + return ( + + ); +}; diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index 8d55b873372f..4b53da357658 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -27,6 +27,7 @@ export interface QueryEditorProps { indexPatterns: Array; dataSource?: DataSource; query: Query; + container?: HTMLDivElement; dataSourceContainerRef?: React.RefCallback; containerRef?: React.RefCallback; settings: Settings; diff --git a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx index 244f4296216c..1693c8f1f447 100644 --- a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx @@ -57,6 +57,7 @@ export type StatefulSearchBarProps = SearchBarOwnProps & { useDefaultBehaviors?: boolean; savedQueryId?: string; onSavedQueryIdChange?: (savedQueryId?: string) => void; + queryEditorComponent?: HTMLDivElement; }; // Respond to user changing the filters 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 11914f134443..c3b6997d4396 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -96,6 +96,7 @@ export interface SearchBarOwnProps { onRefresh?: (payload: { dateRange: TimeRange }) => void; indicateNoData?: boolean; + queryEditorComponent?: HTMLDivElement; } export type SearchBarProps = SearchBarOwnProps & SearchBarInjectedDeps; From b2da62468da5419bf64c9c645f11a6d4408ff819 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Tue, 16 Jul 2024 00:37:37 +0000 Subject: [PATCH 08/11] continue working on footer service Signed-off-by: abbyhu2000 --- src/plugins/data/public/ui/query_editor/query_editor.tsx | 8 ++++++-- .../query_editor_extensions/query_editor_extensions.tsx | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index 4b53da357658..4f03fb5913fb 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -379,12 +379,16 @@ export default class QueryEditorUI extends Component { }} /> )} -
+ {this.state.isCollapsed && {this.props.filterBar}} {this.renderQueryEditorExtensions()} diff --git a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx index 63da4432d402..d08c799e1ab7 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx @@ -31,8 +31,6 @@ const QueryEditorExtensions: React.FC = React.memo(( return Object.values(configMap).sort((a, b) => a.order - b.order); }, [configMap]); - console.log('sortedConfigs', sortedConfigs); - return ( <> {sortedConfigs.map((config) => ( From 0302a67902bc2ee081e99f1111fa229d8396d055 Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Tue, 16 Jul 2024 18:56:14 +0000 Subject: [PATCH 09/11] footer bar Signed-off-by: abbyhu2000 --- .../components/query_editor_footer.tsx | 24 ++--- .../data/public/ui/query_editor/index.tsx | 8 ++ .../ui/query_editor/language_selector.tsx | 34 ++++--- .../public/ui/query_editor/query_editor.tsx | 92 +++++++++++++++---- .../ui/query_editor/query_editor_top_row.tsx | 2 + .../ui/search_bar/create_search_bar.tsx | 9 ++ .../data/public/ui/search_bar/search_bar.tsx | 2 + src/plugins/data/public/ui/types.ts | 3 + src/plugins/data/public/ui/ui_service.ts | 10 +- 9 files changed, 139 insertions(+), 45 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx b/src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx index 80480c3300dd..e2582204c024 100644 --- a/src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx +++ b/src/plugins/data/public/ui/query_editor/components/query_editor_footer.tsx @@ -76,16 +76,18 @@ export const QueryEditorFooter = (props: Props) => { uiService.Settings.setUserQueryLanguage(props.language); return ( - +
+ +
); }; diff --git a/src/plugins/data/public/ui/query_editor/index.tsx b/src/plugins/data/public/ui/query_editor/index.tsx index bddef49af1d4..cd5924acc81f 100644 --- a/src/plugins/data/public/ui/query_editor/index.tsx +++ b/src/plugins/data/public/ui/query_editor/index.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { withOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; import type { QueryEditorTopRowProps } from './query_editor_top_row'; import type { QueryEditorProps } from './query_editor'; +import { QueryLanguageSelectorProps } from './language_selector'; const Fallback = () =>
; @@ -26,3 +27,10 @@ export const QueryEditor = (props: QueryEditorProps) => ( export type { QueryEditorProps }; export { QueryEditorExtensions, QueryEditorExtensionConfig } from './query_editor_extensions'; + +const LazyQueryLanguageSelector = React.lazy(() => import('./language_selector')); +export const QueryLanguageSelector = (props: QueryLanguageSelectorProps) => ( + }> + + +); diff --git a/src/plugins/data/public/ui/query_editor/language_selector.tsx b/src/plugins/data/public/ui/query_editor/language_selector.tsx index 7ea82fe2b24e..bc2fc34c7670 100644 --- a/src/plugins/data/public/ui/query_editor/language_selector.tsx +++ b/src/plugins/data/public/ui/query_editor/language_selector.tsx @@ -8,11 +8,12 @@ import { i18n } from '@osd/i18n'; import React from 'react'; import { getUiService } from '../../services'; -interface Props { +export interface QueryLanguageSelectorProps { language: string; onSelectLanguage: (newLanguage: string) => void; anchorPosition?: PopoverAnchorPosition; appName?: string; + languageSelectorContainerRef?: React.RefCallback; } const mapExternalLanguageToOptions = (language: string) => { @@ -22,7 +23,8 @@ const mapExternalLanguageToOptions = (language: string) => { }; }; -export const QueryLanguageSelector = (props: Props) => { +export const QueryLanguageSelector = (props: QueryLanguageSelectorProps) => { + const ref = React.createRef(); const dqlLabel = i18n.translate('data.query.queryEditor.dqlLanguageName', { defaultMessage: 'DQL', }); @@ -73,16 +75,22 @@ export const QueryLanguageSelector = (props: Props) => { uiService.Settings.setUserQueryLanguage(props.language); return ( - +
+ +
); }; + +// Needed for React.lazy +// eslint-disable-next-line import/no-default-export +export default QueryLanguageSelector; diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index 4f03fb5913fb..bdec768be8b2 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -3,12 +3,27 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiFlexGroup, EuiFlexItem, htmlIdGenerator, PopoverAnchorPosition } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + htmlIdGenerator, + PopoverAnchorPosition, +} from '@elastic/eui'; import classNames from 'classnames'; -import { isEqual } from 'lodash'; -import React, { Component, createRef, RefObject } from 'react'; +import { debounce, isEqual } from 'lodash'; +import React, { Component, createRef, RefObject, useRef } from 'react'; +import { monaco } from 'packages/osd-monaco/target'; import { Settings } from '..'; -import { DataSource, IDataPluginServices, IIndexPattern, Query, TimeRange } from '../..'; +import { + DataSource, + IDataPluginServices, + IFieldType, + IIndexPattern, + Query, + TimeRange, +} from '../..'; import { CodeEditor, OpenSearchDashboardsReactContextValue, @@ -30,6 +45,7 @@ export interface QueryEditorProps { container?: HTMLDivElement; dataSourceContainerRef?: React.RefCallback; containerRef?: React.RefCallback; + languageSelectorContainerRef?: React.RefCallback; settings: Settings; disableAutoFocus?: boolean; screenTitle?: string; @@ -66,6 +82,8 @@ interface State { suggestions: QuerySuggestion[]; indexPatterns: IIndexPattern[]; isCollapsed: boolean; + timeStamp: IFieldType | null; + lineCount: number | undefined; } const KEY_CODES = { @@ -92,9 +110,11 @@ export default class QueryEditorUI extends Component { suggestions: [], indexPatterns: [], isCollapsed: true, + timeStamp: null, + lineCount: undefined, }; - public inputRef: HTMLElement | null = null; + public inputRef: monaco.editor.IStandaloneCodeEditor | null = null; private persistedLog: PersistedLog | undefined; private abortController?: AbortController; @@ -103,6 +123,7 @@ export default class QueryEditorUI extends Component { private headerRef: RefObject = createRef(); private bannerRef: RefObject = createRef(); private footerRef: RefObject = createRef(); + //private codeEditorRef: RefObject = createRef(); private extensionMap = this.props.settings?.getQueryEditorExtensionMap(); private getQueryString = () => { @@ -189,6 +210,12 @@ export default class QueryEditorUI extends Component { private onInputChange = (value: string) => { this.onQueryStringChange(value); + + if (!this.inputRef) return; + + const currentLineCount = this.inputRef.getModel()?.getLineCount(); + if (this.state.lineCount === currentLineCount) return; + this.setState({ lineCount: currentLineCount }); }; private onClickInput = (event: React.MouseEvent) => { @@ -273,7 +300,8 @@ export default class QueryEditorUI extends Component { } this.initPersistedLog(); - // this.fetchIndexPatterns().then(this.updateSuggestions); + //this.fetchIndexPatterns(); + //this.fetchIndexPatterns().then(this.updateTimeStampField); this.initDataSourcesVisibility(); this.initDataSetsVisibility(); } @@ -298,6 +326,11 @@ export default class QueryEditorUI extends Component { } }; + editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor) => { + this.setState({ lineCount: editor.getModel()?.getLineCount() }); + this.inputRef = editor; + }; + public render() { const className = classNames(this.props.className); const headerClassName = classNames('osdQueryEditorHeader', this.props.headerClassName); @@ -314,6 +347,8 @@ export default class QueryEditorUI extends Component { // console.log('this.props.dataSourceContainerRef', this.props.dataSourceContainerRef); // console.log('this.props.containerRef', this.props.containerRef); + console.log('index patterns', this.props.indexPatterns); + console.log('timestamp', this.state.indexPatterns[0]?.timeFieldName); return (
@@ -333,14 +368,7 @@ export default class QueryEditorUI extends Component {
)} - - - + {this.state.isDataSetsVisible && (
@@ -365,6 +393,7 @@ export default class QueryEditorUI extends Component { languageId="opensearchql" value={this.getQueryString()} onChange={this.onInputChange} + editorDidMount={this.editorDidMount} options={{ lineNumbers: 'on', lineHeight: 24, @@ -379,14 +408,37 @@ export default class QueryEditorUI extends Component { }} /> )} +
+ // className={ + // this.state.isCollapsed && useQueryEditor + // ? footerClassName + // : 'osdQueryEditorFooterHide' + // } + > + + + + + + + + {this.state.lineCount} + + {typeof this.props.indexPatterns[0] !== 'string' && + this.props.indexPatterns[0].timeFieldName} + + + + +
{this.state.isCollapsed && {this.props.filterBar}} diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index 416460499aa6..a0e76b291e56 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -40,6 +40,7 @@ export interface QueryEditorTopRowProps { query?: Query; dataSourceContainerRef?: React.RefCallback; containerRef?: React.RefCallback; + languageSelectorContainerRef?: React.RefCallback; settings?: Settings; onSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; onChange: (payload: { dateRange: TimeRange; query?: Query }) => void; @@ -237,6 +238,7 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { query={parsedQuery} dataSourceContainerRef={props.dataSourceContainerRef} containerRef={props.containerRef} + languageSelectorContainerRef={props.languageSelectorContainerRef} settings={props.settings!} screenTitle={props.screenTitle} onChange={onQueryChange} diff --git a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx index 1693c8f1f447..b1191d3ad1e9 100644 --- a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx @@ -50,6 +50,7 @@ interface StatefulSearchBarDeps { settings: Settings; setDataSourceContainerRef: (ref: HTMLDivElement | null) => void; setContainerRef: (ref: HTMLDivElement | null) => void; + setLanguageSelectorContainerRef: (ref: HTMLDivElement | undefined) => void; } export type StatefulSearchBarProps = SearchBarOwnProps & { @@ -142,6 +143,7 @@ export function createSearchBar({ settings, setDataSourceContainerRef, setContainerRef, + setLanguageSelectorContainerRef, }: StatefulSearchBarDeps) { // App name should come from the core application service. // Until it's available, we'll ask the user to provide it for the pre-wired component. @@ -183,6 +185,12 @@ export function createSearchBar({ } }, []); + const languageSelectorContainerRef = useCallback((node) => { + if (node) { + setLanguageSelectorContainerRef(node); + } + }, []); + const containerRef = useCallback((node) => { if (node) { setContainerRef(node); @@ -231,6 +239,7 @@ export function createSearchBar({ settings={settings} dataSourceContainerRef={dataSourceContainerRef} containerRef={containerRef} + languageSelectorContainerRef={languageSelectorContainerRef} onFiltersUpdated={defaultFiltersUpdated(data.query)} onRefreshChange={defaultOnRefreshChange(data.query)} savedQuery={savedQuery} 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 c3b6997d4396..0e0462bcd5dd 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -82,6 +82,7 @@ export interface SearchBarOwnProps { settings?: Settings; dataSourceContainerRef?: React.RefCallback; containerRef?: React.RefCallback; + languageSelectorContainerRef?: React.RefCallback; // Show when user has privileges to save showSaveQuery?: boolean; savedQuery?: SavedQuery; @@ -494,6 +495,7 @@ class SearchBarUI extends Component { timeHistory={this.props.timeHistory} dataSourceContainerRef={this.props.dataSourceContainerRef} containerRef={this.props.containerRef} + languageSelectorContainerRef={this.props.languageSelectorContainerRef} settings={this.props.settings} query={this.state.query} screenTitle={this.props.screenTitle} diff --git a/src/plugins/data/public/ui/types.ts b/src/plugins/data/public/ui/types.ts index fe30220d18cb..1efedad89d5c 100644 --- a/src/plugins/data/public/ui/types.ts +++ b/src/plugins/data/public/ui/types.ts @@ -10,6 +10,7 @@ import { StatefulSearchBarProps } from './search_bar'; import { QueryEditorExtensionConfig } from './query_editor/query_editor_extensions'; import { Settings } from './settings'; import { SuggestionsComponentProps } from './typeahead/suggestions_component'; +import { QueryLanguageSelectorProps } from './query_editor/language_selector'; export * from './settings'; @@ -66,6 +67,8 @@ export interface IUiStart { IndexPatternSelect: React.ComponentType; SearchBar: React.ComponentType; SuggestionsComponent: React.ComponentType; + QueryLanguageSelector: React.ComponentType; + languageSelectorContainer$: Observable; Settings: Settings; dataSourceContainer$: Observable; dataSourceFooter$: Observable; diff --git a/src/plugins/data/public/ui/ui_service.ts b/src/plugins/data/public/ui/ui_service.ts index 314a9323ed81..7559504f969e 100644 --- a/src/plugins/data/public/ui/ui_service.ts +++ b/src/plugins/data/public/ui/ui_service.ts @@ -9,7 +9,7 @@ import { IStorageWrapper } from '../../../opensearch_dashboards_utils/public'; import { ConfigSchema } from '../../config'; import { DataPublicPluginStart } from '../types'; import { createIndexPatternSelect } from './index_pattern_select'; -import { QueryEditorExtensionConfig } from './query_editor'; +import { QueryEditorExtensionConfig, QueryLanguageSelector } from './query_editor'; import { createSearchBar } from './search_bar/create_search_bar'; import { createSettings } from './settings'; import { SuggestionsComponent } from './typeahead'; @@ -32,6 +32,7 @@ export class UiService implements Plugin { private dataSourceContainer$ = new BehaviorSubject(null); private dataSourceFooter$ = new BehaviorSubject(null); private container$ = new BehaviorSubject(null); + private languageSelectorContainer$ = new BehaviorSubject(null); constructor(initializerContext: PluginInitializerContext) { const { enhancements } = initializerContext.config.get(); @@ -72,6 +73,10 @@ export class UiService implements Plugin { this.container$.next(ref); }; + const setLanguageSelectorContainerRef = (ref: HTMLDivElement | null) => { + this.languageSelectorContainer$.next(ref); + }; + const SearchBar = createSearchBar({ core, data: dataServices, @@ -79,12 +84,15 @@ export class UiService implements Plugin { settings: Settings, setDataSourceContainerRef, setContainerRef, + setLanguageSelectorContainerRef, }); return { IndexPatternSelect: createIndexPatternSelect(core.savedObjects.client), SearchBar, SuggestionsComponent, + QueryLanguageSelector, + languageSelectorContainer$: this.languageSelectorContainer$, Settings, dataSourceContainer$: this.dataSourceContainer$, dataSourceFooter$: this.dataSourceFooter$, From c51ed7ccec4d85b8d922b46b38f540b4112f9a6d Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Tue, 16 Jul 2024 20:19:06 +0000 Subject: [PATCH 10/11] move timer picker to be on top Signed-off-by: abbyhu2000 --- .../public/ui/query_editor/_query_editor.scss | 1 + .../public/ui/query_editor/query_editor.tsx | 59 +++++++++++++++---- .../ui/query_editor/query_editor_top_row.tsx | 2 +- .../saved_query_management_component.tsx | 1 - 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/_query_editor.scss b/src/plugins/data/public/ui/query_editor/_query_editor.scss index 314d38334d8a..fd11e76ffaa4 100644 --- a/src/plugins/data/public/ui/query_editor/_query_editor.scss +++ b/src/plugins/data/public/ui/query_editor/_query_editor.scss @@ -46,6 +46,7 @@ .osdQueryEditor__dataSetWrapper { .dataExplorerDSSelect { border-bottom: $euiBorderThin !important; + max-width: 375px; div:is([class$="--group"]) { padding: 0 !important; diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index bdec768be8b2..f28625a158c4 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -36,7 +36,6 @@ import { fetchIndexPatterns } from './fetch_index_patterns'; import { QueryLanguageSelector } from './language_selector'; import { QueryEditorExtensions } from './query_editor_extensions'; import { QueryEditorBtnCollapse } from './query_editor_btn_collapse'; -import { CollapsedQueryBarInput } from './collapsed_query_bar'; export interface QueryEditorProps { indexPatterns: Array; @@ -218,6 +217,13 @@ export default class QueryEditorUI extends Component { this.setState({ lineCount: currentLineCount }); }; + private onSingleLineInputChange = (value: string) => { + // Replace new lines with an empty string to prevent multi-line input + this.onQueryStringChange(value.replace(/[\r\n]+/gm, '')); + + this.setState({ lineCount: undefined }); + }; + private onClickInput = (event: React.MouseEvent) => { if (event.target instanceof HTMLTextAreaElement) { this.onQueryStringChange(event.target.value); @@ -362,26 +368,57 @@ export default class QueryEditorUI extends Component { isCollapsed={this.state.isCollapsed} /> - {this.props.prepend} {this.state.isDataSourcesVisible && ( - +
)} {this.state.isDataSetsVisible && ( - +
)} {(!this.state.isCollapsed || !useQueryEditor) && ( - - + {/* */} + + + + )} + {!useQueryEditor && ( + + )} + {this.props.prepend} @@ -411,11 +448,11 @@ export default class QueryEditorUI extends Component {
diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index a0e76b291e56..97cd16cbe3c8 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -378,11 +378,11 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { direction="column" justifyContent="flexEnd" > + {renderUpdateButton()} {renderQueryEditor()} {renderSharingMetaFields()} - {renderUpdateButton()} 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 da7c25254adb..ebd76ff5b756 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 @@ -182,7 +182,6 @@ export function SavedQueryManagementComponent({ data-test-subj="saved-query-management-popover-button" > - ); From 0078f659dbbe96a4648d117e66476808a8234a5e Mon Sep 17 00:00:00 2001 From: abbyhu2000 Date: Tue, 16 Jul 2024 20:48:20 +0000 Subject: [PATCH 11/11] removeFooterRef Signed-off-by: abbyhu2000 --- .../data/public/ui/query_editor/query_editor.tsx | 3 --- .../query_editor_extensions/query_editor_extension.tsx | 5 ----- .../query_editor_extensions.tsx | 10 +--------- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index f28625a158c4..e09e7d097728 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -161,7 +161,6 @@ export default class QueryEditorUI extends Component { !( this.headerRef.current && this.bannerRef.current && - this.footerRef.current && this.props.queryLanguage && this.extensionMap && Object.keys(this.extensionMap).length > 0 @@ -175,7 +174,6 @@ export default class QueryEditorUI extends Component { configMap={this.extensionMap} componentContainer={this.headerRef.current} bannerContainer={this.bannerRef.current} - footerContainer={this.footerRef.current} indexPatterns={this.props.indexPatterns} dataSource={this.props.dataSource} /> @@ -447,7 +445,6 @@ export default class QueryEditorUI extends Component { )}
= (props) console.log('props.config', props.config); console.log('banner', props.bannerContainer, banner); console.log('component', props.componentContainer, component); - console.log('footer', props.footerContainer, component); if (!isEnabled) return null; return ( @@ -121,9 +119,6 @@ export const QueryEditorExtension: React.FC = (props) {component} - - {footer} - ); }; diff --git a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx index d08c799e1ab7..45b0d076877e 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extensions.tsx @@ -14,17 +14,10 @@ interface QueryEditorExtensionsProps extends QueryEditorExtensionDependencies { configMap?: Record; componentContainer: Element; bannerContainer: Element; - footerContainer: Element; } const QueryEditorExtensions: React.FC = React.memo((props) => { - const { - configMap, - componentContainer, - bannerContainer, - footerContainer, - ...dependencies - } = props; + const { configMap, componentContainer, bannerContainer, ...dependencies } = props; const sortedConfigs = useMemo(() => { if (!configMap || Object.keys(configMap).length === 0) return []; @@ -40,7 +33,6 @@ const QueryEditorExtensions: React.FC = React.memo(( dependencies={dependencies} componentContainer={componentContainer} bannerContainer={bannerContainer} - footerContainer={footerContainer} /> ))}