Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New query bar #30

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
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
Loading