Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/plugins/data/public/ui/query_editor/_query_editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
// overflow: auto;
}

.osdQueryEditorFooterHide {
display: none
}

.osdQueryEditor__languageWrapper {
:first-child {
box-shadow: none !important;
Expand Down Expand Up @@ -42,6 +46,7 @@
.osdQueryEditor__dataSetWrapper {
.dataExplorerDSSelect {
border-bottom: $euiBorderThin !important;
max-width: 375px;

div:is([class$="--group"]) {
padding: 0 !important;
Expand Down
55 changes: 55 additions & 0 deletions src/plugins/data/public/ui/query_editor/collapsed_query_bar.tsx
Original file line number Diff line number Diff line change
@@ -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<CollapsedQueryBarInputProps> = (props) => {
const [isSuggestionsVisible, setIsSuggestionsVisible] = useState(false);
const [suggestionIndex, setSuggestionIndex] = useState<number | null>(null);
const [value, setValue] = useState(props.initialValue ?? '');

return (
<EuiOutsideClickDetector onOutsideClick={() => setIsSuggestionsVisible(false)}>
<div>
<EuiFieldText
data-test-subj="collapsed-query-bar-input-field-text"
value={value}
onClick={() => setIsSuggestionsVisible(true)}
onChange={(e) => setValue(e.target.value)}
onKeyDown={() => setIsSuggestionsVisible(true)}
placeholder={''}
fullWidth
/>
<EuiPortal>
<SuggestionsComponent
show={isSuggestionsVisible}
suggestions={[]}
index={suggestionIndex}
onClick={(suggestion) => {
return;
}}
onMouseEnter={(i) => setSuggestionIndex(i)}
loadMore={() => {}}
size="s"
/>
</EuiPortal>
</div>
</EuiOutsideClickDetector>
);
};
Original file line number Diff line number Diff line change
@@ -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 (
<EuiComboBox
fullWidth
className="languageSelector"
data-test-subj="languageSelector"
options={languageOptions}
selectedOptions={[selectedLanguage]}
onChange={handleLanguageChange}
singleSelection={{ asPlainText: true }}
isClearable={false}
async
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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 (
<div>
<EuiComboBox
fullWidth
className="languageSelector"
data-test-subj="languageSelector"
options={languageOptions}
selectedOptions={[selectedLanguage]}
onChange={handleLanguageChange}
singleSelection={{ asPlainText: true }}
isClearable={false}
async
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -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 (
<EuiComboBox
fullWidth
className="languageSelector"
data-test-subj="languageSelector"
options={languageOptions}
selectedOptions={[selectedLanguage]}
onChange={handleLanguageChange}
singleSelection={{ asPlainText: true }}
isClearable={false}
async
/>
);
};
8 changes: 8 additions & 0 deletions src/plugins/data/public/ui/query_editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => <div />;

Expand All @@ -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) => (
<React.Suspense fallback={<Fallback />}>
<LazyQueryLanguageSelector {...props} />
</React.Suspense>
);
Loading