mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ES3][Connectors]Using EuiComboBox with searchable capability as we do in ESS (#199464)
## Summary This PR swaps the `EuiSelectable` by the `EuiComboBox` component as we have in ESS. ### Context: This PR belongs to this initiative https://github.com/elastic/search-team/issues/8000 where we agreed on bring low hanging fruit artefacts from ESS to ES3 before being replace completly with the full new experience which will be later. Therefor we are not investing effort on make the code scalable and reusable at this point.  --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Navarone Feekery <13634519+navarone-feekery@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Rodney Norris <rodney@tattdcodemonkey.com>
This commit is contained in:
parent
55d5c7ab6b
commit
6bf02e4c13
3 changed files with 174 additions and 32 deletions
|
@ -51,6 +51,14 @@ export const DISABLED_LABEL: string = i18n.translate('xpack.serverlessSearch.dis
|
|||
defaultMessage: 'Disabled',
|
||||
});
|
||||
|
||||
export const BETA_LABEL: string = i18n.translate('xpack.serverlessSearch.beta', {
|
||||
defaultMessage: 'Beta',
|
||||
});
|
||||
|
||||
export const TECH_PREVIEW_LABEL: string = i18n.translate('xpack.serverlessSearch.techPreview', {
|
||||
defaultMessage: 'Tech preview',
|
||||
});
|
||||
|
||||
export const INVALID_JSON_ERROR: string = i18n.translate(
|
||||
'xpack.serverlessSearch.invalidJsonError',
|
||||
{
|
||||
|
|
|
@ -6,10 +6,30 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { EuiFlexItem, EuiFlexGroup, EuiIcon, EuiFormRow, EuiSuperSelect } from '@elastic/eui';
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import {
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiIcon,
|
||||
EuiFormRow,
|
||||
EuiComboBox,
|
||||
EuiBadge,
|
||||
EuiComboBoxOptionOption,
|
||||
EuiText,
|
||||
useEuiTheme,
|
||||
EuiTextTruncate,
|
||||
EuiBadgeGroup,
|
||||
} from '@elastic/eui';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Connector } from '@kbn/search-connectors';
|
||||
import { Connector as BaseConnector } from '@kbn/search-connectors';
|
||||
import { css } from '@emotion/react';
|
||||
import { useAssetBasePath } from '../../hooks/use_asset_base_path';
|
||||
|
||||
import { BETA_LABEL, TECH_PREVIEW_LABEL } from '../../../../common/i18n_string';
|
||||
|
||||
interface Connector extends BaseConnector {
|
||||
iconPath?: string;
|
||||
}
|
||||
import { useKibanaServices } from '../../hooks/use_kibana';
|
||||
import { useConnectorTypes } from '../../hooks/api/use_connector_types';
|
||||
import { useConnector } from '../../hooks/api/use_connector';
|
||||
|
@ -18,6 +38,13 @@ interface EditServiceTypeProps {
|
|||
connector: Connector;
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
interface ConnectorDataSource {
|
||||
_icon: React.ReactNode[];
|
||||
_badges: React.ReactNode;
|
||||
serviceType: string;
|
||||
}
|
||||
|
||||
type ExpandedComboBoxOption = EuiComboBoxOptionOption<ConnectorDataSource>;
|
||||
|
||||
interface GeneratedConnectorNameResult {
|
||||
connectorName: string;
|
||||
|
@ -29,30 +56,18 @@ export const EditServiceType: React.FC<EditServiceTypeProps> = ({ connector, isD
|
|||
const connectorTypes = useConnectorTypes();
|
||||
const queryClient = useQueryClient();
|
||||
const { queryKey } = useConnector(connector.id);
|
||||
const assetBasePath = useAssetBasePath();
|
||||
|
||||
const options =
|
||||
connectorTypes.map((connectorType) => ({
|
||||
inputDisplay: (
|
||||
<EuiFlexGroup direction="row" alignItems="center">
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
data-test-subj={`serverlessSearchConnectorServiceType-${connectorType.serviceType}`}
|
||||
>
|
||||
<EuiIcon
|
||||
size="l"
|
||||
title={connectorType.name}
|
||||
id={connectorType.serviceType}
|
||||
type={connectorType.iconPath}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{connectorType.name}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
),
|
||||
value: connectorType.serviceType,
|
||||
})) || [];
|
||||
const allConnectors = useMemo(
|
||||
() => connectorTypes.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
[connectorTypes]
|
||||
);
|
||||
|
||||
const { isLoading, mutate } = useMutation({
|
||||
mutationFn: async (inputServiceType: string) => {
|
||||
if (inputServiceType === null || inputServiceType === '') {
|
||||
return { serviceType: inputServiceType, name: connector.name };
|
||||
}
|
||||
const body = { service_type: inputServiceType };
|
||||
await http.post(`/internal/serverless_search/connectors/${connector.id}/service_type`, {
|
||||
body: JSON.stringify(body),
|
||||
|
@ -99,6 +114,104 @@ export const EditServiceType: React.FC<EditServiceTypeProps> = ({ connector, isD
|
|||
},
|
||||
});
|
||||
|
||||
const getInitialOptions = (): ExpandedComboBoxOption[] => {
|
||||
return allConnectors.map((conn, key) => {
|
||||
const _icon: React.ReactNode[] = [];
|
||||
let _ariaLabelAppend = '';
|
||||
if (conn.isTechPreview) {
|
||||
_icon.push(
|
||||
<EuiBadge
|
||||
aria-label={TECH_PREVIEW_LABEL}
|
||||
key={key + '-preview'}
|
||||
iconType="beaker"
|
||||
color="hollow"
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.serverlessSearch.connectors.chooseConnectorSelectable.thechPreviewBadgeLabel',
|
||||
{ defaultMessage: 'Tech preview' }
|
||||
)}
|
||||
</EuiBadge>
|
||||
);
|
||||
_ariaLabelAppend += ` ${TECH_PREVIEW_LABEL}`;
|
||||
}
|
||||
if (conn.isBeta) {
|
||||
_icon.push(
|
||||
<EuiBadge aria-label={BETA_LABEL} key={key + '-beta'} iconType={'beta'} color="hollow">
|
||||
{BETA_LABEL}
|
||||
</EuiBadge>
|
||||
);
|
||||
_ariaLabelAppend += ` ${BETA_LABEL}`;
|
||||
}
|
||||
return {
|
||||
key: key.toString(),
|
||||
label: conn.name,
|
||||
value: {
|
||||
_icon,
|
||||
_badges: <EuiIcon size="l" type={conn.iconPath} />,
|
||||
serviceType: conn.serviceType,
|
||||
},
|
||||
'aria-label': conn.name + _ariaLabelAppend,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const initialOptions = getInitialOptions();
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const renderOption = (
|
||||
option: ExpandedComboBoxOption,
|
||||
searchValue: string,
|
||||
contentClassName: string
|
||||
) => {
|
||||
const {
|
||||
value: { _icon, _badges, serviceType } = { _icon: [], _badges: null, serviceType: '' },
|
||||
key,
|
||||
label,
|
||||
} = option;
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
className={contentClassName}
|
||||
key={key + '-span'}
|
||||
gutterSize="m"
|
||||
responsive={false}
|
||||
direction="row"
|
||||
>
|
||||
<EuiFlexItem grow={false}>{_badges}</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
css={css`
|
||||
overflow: auto;
|
||||
`}
|
||||
grow
|
||||
data-test-subj={`serverlessSearchConnectorServiceType-${serviceType}`}
|
||||
>
|
||||
<EuiText textAlign="left" size="s">
|
||||
<EuiTextTruncate text={label} truncation="end" />
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadgeGroup gutterSize="xs">{_icon}</EuiBadgeGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
const onSelectedOptionChange = useCallback(
|
||||
(selectedItem: Array<EuiComboBoxOptionOption<ConnectorDataSource>>) => {
|
||||
if (selectedItem.length === 0) {
|
||||
return;
|
||||
}
|
||||
const keySelected = Number(selectedItem[0].key);
|
||||
mutate(allConnectors[keySelected].serviceType);
|
||||
},
|
||||
[mutate, allConnectors]
|
||||
);
|
||||
const selectedOptions = useMemo(() => {
|
||||
const selectedOption = initialOptions.find(
|
||||
(option) => option.value?.serviceType === connector.service_type
|
||||
);
|
||||
return selectedOption ? [selectedOption] : [];
|
||||
}, [initialOptions, connector.service_type]);
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.serverlessSearch.connectors.serviceTypeLabel', {
|
||||
|
@ -107,15 +220,36 @@ export const EditServiceType: React.FC<EditServiceTypeProps> = ({ connector, isD
|
|||
data-test-subj="serverlessSearchEditConnectorType"
|
||||
fullWidth
|
||||
>
|
||||
<EuiSuperSelect
|
||||
// We only want to allow people to set the service type once to avoid weird conflicts
|
||||
disabled={Boolean(connector.service_type) || isDisabled}
|
||||
data-test-subj="serverlessSearchEditConnectorTypeChoices"
|
||||
<EuiComboBox<ConnectorDataSource>
|
||||
aria-label={i18n.translate(
|
||||
'xpack.serverlessSearch.connectors.chooseConnectorSelectable.euiComboBox.accessibleScreenReaderLabelLabel',
|
||||
{ defaultMessage: 'Select a data source for your connector to use.' }
|
||||
)}
|
||||
isDisabled={Boolean(connector.service_type) || isDisabled}
|
||||
isLoading={isLoading}
|
||||
onChange={(event) => mutate(event)}
|
||||
options={options}
|
||||
valueOfSelected={connector.service_type || undefined}
|
||||
data-test-subj="serverlessSearchEditConnectorTypeChoices"
|
||||
prepend={
|
||||
<EuiIcon
|
||||
type={
|
||||
connector.service_type
|
||||
? connectorTypes.find((conn) => conn.serviceType === connector.service_type)
|
||||
?.iconPath ?? ''
|
||||
: `${assetBasePath}/connectors.svg`
|
||||
}
|
||||
size="l"
|
||||
/>
|
||||
}
|
||||
singleSelection={{ asPlainText: true }}
|
||||
fullWidth
|
||||
placeholder={i18n.translate(
|
||||
'xpack.serverlessSearch.connectors.chooseConnectorSelectable.placeholder.text',
|
||||
{ defaultMessage: 'Choose a data source' }
|
||||
)}
|
||||
options={initialOptions}
|
||||
selectedOptions={selectedOptions}
|
||||
onChange={onSelectedOptionChange}
|
||||
renderOption={renderOption}
|
||||
rowHeight={(euiTheme.base / 2) * 5}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
);
|
||||
|
|
|
@ -64,9 +64,9 @@ export function SvlSearchConnectorsPageProvider({ getService }: FtrProviderConte
|
|||
await testSubjects.existOrFail('serverlessSearchEditConnectorType');
|
||||
await testSubjects.existOrFail('serverlessSearchEditConnectorTypeChoices');
|
||||
await testSubjects.click('serverlessSearchEditConnectorTypeChoices');
|
||||
await testSubjects.exists('serverlessSearchConnectorServiceType-zoom');
|
||||
await testSubjects.setValue('serverlessSearchEditConnectorTypeChoices', type);
|
||||
await testSubjects.exists(`serverlessSearchConnectorServiceType-${type}`);
|
||||
await testSubjects.click(`serverlessSearchConnectorServiceType-${type}`);
|
||||
await testSubjects.existOrFail('serverlessSearchConnectorServiceType-zoom');
|
||||
},
|
||||
async expectConnectorIdToMatchUrl(connectorId: string) {
|
||||
expect(await browser.getCurrentUrl()).contain(`/app/connectors/${connectorId}`);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue