Skip to content

Commit

Permalink
[8.x] [Inference Connector] Changed UI/UX due to the new RFC for the …
Browse files Browse the repository at this point in the history
…_inference/_service (elastic#203363) (elastic#204690)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Inference Connector] Changed UI/UX due to the new RFC for the
_inference/_service
(elastic#203363)](elastic#203363)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Yuliia
Naumenko","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-12-13T16:00:59Z","message":"[Inference
Connector] Changed UI/UX due to the new RFC for the _inference/_service
(elastic#203363)\n\nRelated
RFC\r\nhttps://docs.google.com/document/d/1DbWpqEKM-MJR2cSJNKLC7RcCNXXQ-_iUShsXKRFKXfk/edit?tab=t.0\r\n\r\n##
Summary\r\n\r\n- removed Task Settings from the UI and schema
definition, due to the\r\ndiscussion on Inference sync Dec 5th.\r\n-
renamed provider to service\r\n- added name and description, use name
for the service selector user\r\nfriendly way\r\n- dropped options and
display type dropdown select, use freeform text\r\ninput instead\r\n-
dropped `display` field type, renamed `tooltip` to the
`description`.\r\nProperly updated `ConnectorConfigurationFormItems`
in\r\n`x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx`\r\n\r\nUI
with the updates:\r\n<img width=\"1281\" alt=\"Screenshot 2024-12-08 at
10 09
52 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/fdb17dd4-c8e4-496b-85e7-03c363546b8e\">\r\n\r\n---------\r\n\r\nCo-authored-by:
Ying
<[email protected]>","sha":"5b108453ffe823b5d559952f37da1030a79d3352","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport
missing","v9.0.0","backport:version","v8.18.0"],"number":203363,"url":"https://github.com/elastic/kibana/pull/203363","mergeCommit":{"message":"[Inference
Connector] Changed UI/UX due to the new RFC for the _inference/_service
(elastic#203363)\n\nRelated
RFC\r\nhttps://docs.google.com/document/d/1DbWpqEKM-MJR2cSJNKLC7RcCNXXQ-_iUShsXKRFKXfk/edit?tab=t.0\r\n\r\n##
Summary\r\n\r\n- removed Task Settings from the UI and schema
definition, due to the\r\ndiscussion on Inference sync Dec 5th.\r\n-
renamed provider to service\r\n- added name and description, use name
for the service selector user\r\nfriendly way\r\n- dropped options and
display type dropdown select, use freeform text\r\ninput instead\r\n-
dropped `display` field type, renamed `tooltip` to the
`description`.\r\nProperly updated `ConnectorConfigurationFormItems`
in\r\n`x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx`\r\n\r\nUI
with the updates:\r\n<img width=\"1281\" alt=\"Screenshot 2024-12-08 at
10 09
52 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/fdb17dd4-c8e4-496b-85e7-03c363546b8e\">\r\n\r\n---------\r\n\r\nCo-authored-by:
Ying
<[email protected]>","sha":"5b108453ffe823b5d559952f37da1030a79d3352"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/203363","number":203363,"mergeCommit":{"message":"[Inference
Connector] Changed UI/UX due to the new RFC for the _inference/_service
(elastic#203363)\n\nRelated
RFC\r\nhttps://docs.google.com/document/d/1DbWpqEKM-MJR2cSJNKLC7RcCNXXQ-_iUShsXKRFKXfk/edit?tab=t.0\r\n\r\n##
Summary\r\n\r\n- removed Task Settings from the UI and schema
definition, due to the\r\ndiscussion on Inference sync Dec 5th.\r\n-
renamed provider to service\r\n- added name and description, use name
for the service selector user\r\nfriendly way\r\n- dropped options and
display type dropdown select, use freeform text\r\ninput instead\r\n-
dropped `display` field type, renamed `tooltip` to the
`description`.\r\nProperly updated `ConnectorConfigurationFormItems`
in\r\n`x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx`\r\n\r\nUI
with the updates:\r\n<img width=\"1281\" alt=\"Screenshot 2024-12-08 at
10 09
52 PM\"\r\nsrc=\"https://github.com/user-attachments/assets/fdb17dd4-c8e4-496b-85e7-03c363546b8e\">\r\n\r\n---------\r\n\r\nCo-authored-by:
Ying
<[email protected]>","sha":"5b108453ffe823b5d559952f37da1030a79d3352"}},{"branch":"8.x","label":"v8.18.0","labelRegex":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Yuliia Naumenko <[email protected]>
pgayvallet and YulNaumenko authored Dec 18, 2024
1 parent f535325 commit 246ef2a
Showing 15 changed files with 1,915 additions and 548 deletions.
Original file line number Diff line number Diff line change
@@ -45314,7 +45314,6 @@
"xpack.stackConnectors.components.inference.taskTypeDetailsLabel": "Paramètres des tâches",
"xpack.stackConnectors.components.inference.taskTypeFieldLabel": "Type de tâche",
"xpack.stackConnectors.components.inference.taskTypeHelpLabel": "Configurer la tâche d'inférence. Ces paramètres sont spécifiques au service et au modèle sélectionnés.",
"xpack.stackConnectors.components.inference.taskTypeLabel": "Type de tâche",
"xpack.stackConnectors.components.inference.unableToFindProvidersQueryMessage": "Impossible de trouver des fournisseurs",
"xpack.stackConnectors.components.jira.apiTokenTextFieldLabel": "Token d'API",
"xpack.stackConnectors.components.jira.apiUrlTextFieldLabel": "URL",
Original file line number Diff line number Diff line change
@@ -45163,7 +45163,6 @@
"xpack.stackConnectors.components.inference.taskTypeDetailsLabel": "タスク設定",
"xpack.stackConnectors.components.inference.taskTypeFieldLabel": "タスクタイプ",
"xpack.stackConnectors.components.inference.taskTypeHelpLabel": "推論タスクを構成します。これらの設定は、選択したサービスおよびモデルに固有です。",
"xpack.stackConnectors.components.inference.taskTypeLabel": "タスクタイプ",
"xpack.stackConnectors.components.inference.unableToFindProvidersQueryMessage": "プロバイダーが見つかりません",
"xpack.stackConnectors.components.jira.apiTokenTextFieldLabel": "APIトークン",
"xpack.stackConnectors.components.jira.apiUrlTextFieldLabel": "URL",
Original file line number Diff line number Diff line change
@@ -45261,7 +45261,6 @@
"xpack.stackConnectors.components.inference.taskTypeDetailsLabel": "任务设置",
"xpack.stackConnectors.components.inference.taskTypeFieldLabel": "任务类型",
"xpack.stackConnectors.components.inference.taskTypeHelpLabel": "配置推理任务。这些设置特定于选定服务和模型。",
"xpack.stackConnectors.components.inference.taskTypeLabel": "任务类型",
"xpack.stackConnectors.components.inference.unableToFindProvidersQueryMessage": "找不到提供商",
"xpack.stackConnectors.components.jira.apiTokenTextFieldLabel": "API 令牌",
"xpack.stackConnectors.components.jira.apiUrlTextFieldLabel": "URL",
23 changes: 3 additions & 20 deletions x-pack/plugins/stack_connectors/common/dynamic_config/types.ts
Original file line number Diff line number Diff line change
@@ -5,15 +5,6 @@
* 2.0.
*/

export enum DisplayType {
TEXTBOX = 'textbox',
TEXTAREA = 'textarea',
NUMERIC = 'numeric',
TOGGLE = 'toggle',
DROPDOWN = 'dropdown',
CHECKABLE = 'checkable',
}

export interface SelectOption {
label: string;
value: string;
@@ -28,7 +19,6 @@ export interface Dependency {
export enum FieldType {
STRING = 'str',
INTEGER = 'int',
LIST = 'list',
BOOLEAN = 'bool',
}

@@ -44,21 +34,13 @@ export interface Validation {
}

export interface ConfigProperties {
category?: string;
default_value: string | number | boolean | null;
depends_on: Dependency[];
display: DisplayType;
description: string | null;
label: string;
options?: SelectOption[];
order?: number | null;
placeholder?: string;
required: boolean;
sensitive: boolean;
tooltip: string | null;
updatable: boolean;
type: FieldType;
ui_restrictions: string[];
validations: Validation[];
value: string | number | boolean | null;
}

interface ConfigEntry extends ConfigProperties {
@@ -68,4 +50,5 @@ interface ConfigEntry extends ConfigProperties {
export interface ConfigEntryView extends ConfigEntry {
isValid: boolean;
validationErrors: string[];
value: string | number | boolean | null;
}
12 changes: 4 additions & 8 deletions x-pack/plugins/stack_connectors/common/inference/types.ts
Original file line number Diff line number Diff line change
@@ -40,14 +40,10 @@ export type StreamingResponse = TypeOf<typeof StreamingResponseSchema>;

export type FieldsConfiguration = Record<string, ConfigProperties>;

export interface InferenceTaskType {
task_type: string;
configuration: FieldsConfiguration;
}

export interface InferenceProvider {
provider: string;
task_types: InferenceTaskType[];
service: string;
name: string;
task_types: string[];
logo?: string;
configuration: FieldsConfiguration;
configurations: FieldsConfiguration;
}
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useMemo, useCallback } from 'react';
import React, { useMemo } from 'react';
import { css } from '@emotion/react';

import {
@@ -18,7 +18,6 @@ import {
EuiTextColor,
EuiButtonGroup,
EuiPanel,
EuiHorizontalRule,
EuiButtonEmpty,
EuiCopy,
EuiButton,
@@ -55,7 +54,6 @@ interface AdditionalOptionsConnectorFieldsProps {
onTaskTypeOptionsSelect: (taskType: string, provider?: string) => void;
selectedTaskType?: string;
taskTypeFormFields: ConfigEntryView[];
taskTypeSchema: ConfigEntryView[];
taskTypeOptions: TaskTypeOption[];
}

@@ -65,35 +63,13 @@ export const AdditionalOptionsConnectorFields: React.FC<AdditionalOptionsConnect
isEdit,
taskTypeOptions,
optionalProviderFormFields,
taskTypeFormFields,
taskTypeSchema,
selectedTaskType,
onSetProviderConfigEntry,
onTaskTypeOptionsSelect,
}) => {
const xsFontSize = useEuiFontSize('xs').fontSize;
const { euiTheme } = useEuiTheme();
const { setFieldValue, validateFields } = useFormContext();

const onSetTaskTypeConfigEntry = useCallback(
async (key: string, value: unknown) => {
if (taskTypeSchema) {
const entry: ConfigEntryView | undefined = taskTypeSchema.find(
(p: ConfigEntryView) => p.key === key
);
if (entry) {
if (!config.taskTypeConfig) {
config.taskTypeConfig = {};
}
const newConfig = { ...config.taskTypeConfig };
newConfig[key] = value;
setFieldValue('config.taskTypeConfig', newConfig);
await validateFields(['config.taskTypeConfig']);
}
}
},
[config, setFieldValue, taskTypeSchema, validateFields]
);
const { setFieldValue } = useFormContext();

const taskTypeSettings = useMemo(
() =>
@@ -103,7 +79,7 @@ export const AdditionalOptionsConnectorFields: React.FC<AdditionalOptionsConnect
<h4>
<FormattedMessage
id="xpack.stackConnectors.components.inference.taskTypeDetailsLabel"
defaultMessage="Task settings"
defaultMessage="Task type"
/>
</h4>
</EuiTitle>
@@ -116,7 +92,7 @@ export const AdditionalOptionsConnectorFields: React.FC<AdditionalOptionsConnect
>
<FormattedMessage
id="xpack.stackConnectors.components.inference.taskTypeHelpLabel"
defaultMessage="Configure the inference task. These settings are specific to the service and model selected."
defaultMessage="Configure the inference task. Task types are specific to the service and model selected."
/>
</div>
<EuiSpacer size="m" />
@@ -135,18 +111,7 @@ export const AdditionalOptionsConnectorFields: React.FC<AdditionalOptionsConnect
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);

return (
<EuiFormRow
id="taskType"
fullWidth
label={
<FormattedMessage
id="xpack.stackConnectors.components.inference.taskTypeLabel"
defaultMessage="Task type"
/>
}
isInvalid={isInvalid}
error={errorMessage}
>
<EuiFormRow id="taskType" fullWidth isInvalid={isInvalid} error={errorMessage}>
{isEdit || readOnly ? (
<EuiButton
css={{
@@ -186,23 +151,13 @@ export const AdditionalOptionsConnectorFields: React.FC<AdditionalOptionsConnect
);
}}
</UseField>
<EuiSpacer size="s" />
<ConnectorConfigurationFormItems
itemsGrow={false}
isLoading={false}
direction="column"
items={taskTypeFormFields}
setConfigEntry={onSetTaskTypeConfigEntry}
/>
</>
) : null,
[
selectedTaskType,
config?.taskType,
config.taskType,
xsFontSize,
euiTheme.colors,
taskTypeFormFields,
onSetTaskTypeConfigEntry,
isEdit,
readOnly,
taskTypeOptions,
@@ -276,7 +231,7 @@ export const AdditionalOptionsConnectorFields: React.FC<AdditionalOptionsConnect
) : null}

{taskTypeSettings}
<EuiHorizontalRule />
<EuiSpacer size="m" />
<EuiTitle size="xxs" data-test-subj="task-type-details-label">
<h4>
<FormattedMessage

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -32,7 +32,6 @@ import { useKibana } from '@kbn/triggers-actions-ui-plugin/public';

import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
import { ConfigEntryView } from '../../../common/dynamic_config/types';
import { InferenceTaskType } from '../../../common/inference/types';
import { ServiceProviderKeys } from '../../../common/inference/constants';
import { ConnectorConfigurationFormItems } from '../lib/dynamic_config/connector_configuration_form_items';
import * as i18n from './translations';
@@ -43,11 +42,7 @@ import { generateInferenceEndpointId, getTaskTypeOptions, TaskTypeOption } from
import { useProviders } from './providers/get_providers';
import { SERVICE_PROVIDERS } from './providers/render_service_provider/service_provider';
import { AdditionalOptionsConnectorFields } from './additional_options_fields';
import {
getProviderConfigHiddenField,
getProviderSecretsHiddenField,
getTaskTypeConfigHiddenField,
} from './hidden_fields';
import { getProviderConfigHiddenField, getProviderSecretsHiddenField } from './hidden_fields';

const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps> = ({
readOnly,
@@ -63,7 +58,6 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
watch: [
'secrets.providerSecrets',
'config.taskType',
'config.taskTypeConfig',
'config.inferenceId',
'config.provider',
'config.providerConfig',
@@ -82,10 +76,9 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
[]
);

const [taskTypeSchema, setTaskTypeSchema] = useState<ConfigEntryView[]>([]);
const [taskTypeOptions, setTaskTypeOptions] = useState<TaskTypeOption[]>([]);
const [selectedTaskType, setSelectedTaskType] = useState<string>(DEFAULT_TASK_TYPE);
const [taskTypeFormFields, setTaskTypeFormFields] = useState<ConfigEntryView[]>([]);
const [taskTypeFormFields] = useState<ConfigEntryView[]>([]);

const handleProviderClosePopover = useCallback(() => {
setProviderPopoverOpen(false);
@@ -111,80 +104,50 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
if (isSubmitting) {
validateFields(['config.providerConfig']);
validateFields(['secrets.providerSecrets']);
validateFields(['config.taskTypeConfig']);
}
}, [isSubmitting, config, validateFields]);

const onTaskTypeOptionsSelect = useCallback(
(taskType: string, provider?: string) => {
(taskType: string) => {
// Get task type settings
const currentProvider = providers?.find((p) => p.provider === (provider ?? config?.provider));
const currentTaskTypes = currentProvider?.task_types;
const newTaskType = currentTaskTypes?.find((p) => p.task_type === taskType);

setSelectedTaskType(taskType);

// transform the schema
const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({
key: k,
isValid: true,
...newTaskType?.configuration[k],
})) as ConfigEntryView[];
setTaskTypeSchema(newTaskTypeSchema);

const configDefaults = Object.keys(newTaskType?.configuration ?? {}).reduce(
(res: Record<string, unknown>, k) => {
if (newTaskType?.configuration[k] && !!newTaskType?.configuration[k].default_value) {
res[k] = newTaskType.configuration[k].default_value;
} else {
res[k] = null;
}
return res;
},
{}
);

updateFieldValues({
config: {
taskType,
taskTypeConfig: configDefaults,
},
});
generateInferenceEndpointId(
{ ...config, taskType, taskTypeConfig: configDefaults },
setFieldValue
);
generateInferenceEndpointId({ ...config, taskType }, setFieldValue);
},
[config, providers, setFieldValue, updateFieldValues]
[config, setFieldValue, updateFieldValues]
);

const onProviderChange = useCallback(
(provider?: string) => {
const newProvider = providers?.find((p) => p.provider === provider);
const newProvider = providers?.find((p) => p.service === provider);

// Update task types list available for the selected provider
const providerTaskTypes = (newProvider?.task_types ?? []).map((t) => t.task_type);
setTaskTypeOptions(getTaskTypeOptions(providerTaskTypes));
if (providerTaskTypes.length > 0) {
onTaskTypeOptionsSelect(providerTaskTypes[0], provider);
setTaskTypeOptions(getTaskTypeOptions(newProvider?.task_types ?? []));
if (newProvider?.task_types && newProvider?.task_types.length > 0) {
onTaskTypeOptionsSelect(newProvider?.task_types[0]);
}

// Update connector providerSchema
const newProviderSchema = Object.keys(newProvider?.configuration ?? {}).map((k) => ({
const newProviderSchema = Object.keys(newProvider?.configurations ?? {}).map((k) => ({
key: k,
isValid: true,
...newProvider?.configuration[k],
...newProvider?.configurations[k],
})) as ConfigEntryView[];

setProviderSchema(newProviderSchema);

const defaultProviderConfig: Record<string, unknown> = {};
const defaultProviderSecrets: Record<string, unknown> = {};

Object.keys(newProvider?.configuration ?? {}).forEach((k) => {
if (!newProvider?.configuration[k].sensitive) {
if (newProvider?.configuration[k] && !!newProvider?.configuration[k].default_value) {
defaultProviderConfig[k] = newProvider.configuration[k].default_value;
Object.keys(newProvider?.configurations ?? {}).forEach((k) => {
if (!newProvider?.configurations[k].sensitive) {
if (newProvider?.configurations[k] && !!newProvider?.configurations[k].default_value) {
defaultProviderConfig[k] = newProvider.configurations[k].default_value;
} else {
defaultProviderConfig[k] = null;
}
@@ -195,7 +158,7 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields

updateFieldValues({
config: {
provider: newProvider?.provider,
provider: newProvider?.service,
providerConfig: defaultProviderConfig,
},
secrets: {
@@ -207,31 +170,16 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
);

useEffect(() => {
const getTaskTypeSchema = (taskTypes: InferenceTaskType[]) => {
const newTaskType = taskTypes.find((p) => p.task_type === config?.taskType);

// transform the schema
const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({
key: k,
isValid: true,
...newTaskType?.configuration[k],
})) as ConfigEntryView[];

setTaskTypeSchema(newTaskTypeSchema);
};

if (config?.provider && isEdit) {
const newProvider = providers?.find((p) => p.provider === config.provider);
const newProvider = providers?.find((p) => p.service === config.provider);
// Update connector providerSchema
const newProviderSchema = Object.keys(newProvider?.configuration ?? {}).map((k) => ({
const newProviderSchema = Object.keys(newProvider?.configurations ?? {}).map((k) => ({
key: k,
isValid: true,
...newProvider?.configuration[k],
...newProvider?.configurations[k],
})) as ConfigEntryView[];

setProviderSchema(newProviderSchema);

getTaskTypeSchema(newProvider?.task_types ?? []);
}
}, [config?.provider, config?.taskType, http, isEdit, providers]);

@@ -250,31 +198,14 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
})
: [];

existingConfiguration.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
setOptionalProviderFormFields(existingConfiguration.filter((p) => !p.required && !p.sensitive));
setRequiredProviderFormFields(existingConfiguration.filter((p) => p.required || p.sensitive));
}, [config?.providerConfig, providerSchema, secrets]);

useEffect(() => {
// Set values from the task type config to the schema
const existingTaskTypeConfiguration = taskTypeSchema
? taskTypeSchema.map((item: ConfigEntryView) => {
const itemValue = item;
itemValue.isValid = true;
if (config?.taskTypeConfig) {
itemValue.value = config?.taskTypeConfig[item.key] as any;
}
return itemValue;
})
: [];
existingTaskTypeConfiguration.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
setTaskTypeFormFields(existingTaskTypeConfiguration);
}, [config, taskTypeSchema]);

const getProviderOptions = useCallback(() => {
return providers?.map((p) => ({
label: p.provider,
key: p.provider,
label: p.service,
key: p.service,
})) as EuiSelectableOption[];
}, [providers]);

@@ -433,7 +364,6 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
onSetProviderConfigEntry={onSetProviderConfigEntry}
onTaskTypeOptionsSelect={onTaskTypeOptionsSelect}
taskTypeFormFields={taskTypeFormFields}
taskTypeSchema={taskTypeSchema}
taskTypeOptions={taskTypeOptions}
selectedTaskType={selectedTaskType}
/>
@@ -449,7 +379,6 @@ const InferenceAPIConnectorFields: React.FunctionComponent<ActionConnectorFields
setRequiredProviderFormFields,
isSubmitting
)}
{getTaskTypeConfigHiddenField(taskTypeSchema, setTaskTypeFormFields, isSubmitting)}
</>
) : null}
</>
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ export const getNonEmptyValidator = (
newSchema.push(field);
});

validationEventHandler(newSchema.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)));
validationEventHandler(newSchema);
if (hasErrors) {
return {
code: 'ERR_FIELD_MISSING',
Original file line number Diff line number Diff line change
@@ -57,32 +57,3 @@ export const getProviderConfigHiddenField = (
}}
/>
);

export const getTaskTypeConfigHiddenField = (
taskTypeSchema: ConfigEntryView[],
setTaskTypeFormFields: React.Dispatch<React.SetStateAction<ConfigEntryView[]>>,
isSubmitting: boolean
) => (
<UseField
path="config.taskTypeConfig"
component={HiddenField}
config={{
validations: [
{
validator: getNonEmptyValidator(
taskTypeSchema,
(requiredFormFields) => {
const formFields = [
...requiredFormFields,
...(taskTypeSchema ?? []).filter((f) => !f.required),
];
setTaskTypeFormFields(formFields.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)));
},
isSubmitting
),
isBlocking: true,
},
],
}}
/>
);
Original file line number Diff line number Diff line change
@@ -13,19 +13,11 @@ import {
EuiFieldPassword,
EuiSwitch,
EuiTextArea,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiFieldNumber,
EuiCheckableCard,
useGeneratedHtmlId,
EuiSpacer,
EuiSuperSelect,
EuiText,
} from '@elastic/eui';

import { isEmpty } from 'lodash/fp';
import { ConfigEntryView, DisplayType } from '../../../../common/dynamic_config/types';
import { ConfigEntryView, FieldType } from '../../../../common/dynamic_config/types';
import {
ensureBooleanType,
ensureCorrectTyping,
@@ -49,7 +41,7 @@ export const ConfigInputField: React.FC<ConfigInputFieldProps> = ({
validateAndSetConfigValue,
}) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { isValid, placeholder, value, default_value, key } = configEntry;
const { isValid, value, default_value, key } = configEntry;
const [innerValue, setInnerValue] = useState(
!value || value.toString().length === 0 ? default_value : value
);
@@ -68,7 +60,6 @@ export const ConfigInputField: React.FC<ConfigInputFieldProps> = ({
setInnerValue(event.target.value);
validateAndSetConfigValue(event.target.value);
}}
placeholder={placeholder}
/>
);
};
@@ -104,7 +95,7 @@ export const ConfigInputTextArea: React.FC<ConfigInputFieldProps> = ({
validateAndSetConfigValue,
}) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { isValid, placeholder, value, default_value, key } = configEntry;
const { isValid, value, default_value, key } = configEntry;
const [innerValue, setInnerValue] = useState(value ?? default_value);
useEffect(() => {
setInnerValue(value ?? default_value);
@@ -121,7 +112,6 @@ export const ConfigInputTextArea: React.FC<ConfigInputFieldProps> = ({
setInnerValue(event.target.value);
validateAndSetConfigValue(event.target.value);
}}
placeholder={placeholder}
/>
);
};
@@ -132,7 +122,7 @@ export const ConfigNumberField: React.FC<ConfigInputFieldProps> = ({
validateAndSetConfigValue,
}) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { isValid, placeholder, value, default_value, key } = configEntry;
const { isValid, value, default_value, key } = configEntry;
const [innerValue, setInnerValue] = useState(value ?? default_value);
useEffect(() => {
setInnerValue(!value || value.toString().length === 0 ? default_value : value);
@@ -149,43 +139,10 @@ export const ConfigNumberField: React.FC<ConfigInputFieldProps> = ({
setInnerValue(newValue);
validateAndSetConfigValue(newValue);
}}
placeholder={placeholder}
/>
);
};

export const ConfigCheckableField: React.FC<ConfigInputFieldProps> = ({
configEntry,
validateAndSetConfigValue,
}) => {
const radioCardId = useGeneratedHtmlId({ prefix: 'radioCard' });
// eslint-disable-next-line @typescript-eslint/naming-convention
const { value, options, default_value } = configEntry;
const [innerValue, setInnerValue] = useState(value ?? default_value);
useEffect(() => {
setInnerValue(value ?? default_value);
}, [default_value, value]);
return (
<>
{options?.map((o) => (
<>
<EuiCheckableCard
id={radioCardId}
label={o.label}
value={innerValue as any}
checked={innerValue === o.value}
onChange={(event) => {
setInnerValue(o.value);
validateAndSetConfigValue(o.value);
}}
/>
<EuiSpacer size="s" />
</>
))}
</>
);
};

export const ConfigSensitiveTextArea: React.FC<ConfigInputFieldProps> = ({
isLoading,
configEntry,
@@ -230,44 +187,6 @@ export const ConfigInputPassword: React.FC<ConfigInputFieldProps> = ({
);
};

export const ConfigSelectField: React.FC<ConfigInputFieldProps> = ({
configEntry,
isLoading,
validateAndSetConfigValue,
}) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { isValid, options, value, default_value } = configEntry;
const [innerValue, setInnerValue] = useState(value ?? default_value);
const optionsRes = options?.map((o) => ({
value: o.value,
inputDisplay: (
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
{o.icon ? (
<EuiFlexItem grow={false}>
<EuiIcon color="subdued" style={{ lineHeight: 'inherit' }} type={o.icon} />
</EuiFlexItem>
) : null}
<EuiFlexItem grow={false}>
<EuiText>{o.label}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
),
}));
return (
<EuiSuperSelect
fullWidth
isInvalid={!isValid}
disabled={isLoading}
options={optionsRes as any}
valueOfSelected={innerValue as any}
onChange={(newValue) => {
setInnerValue(newValue);
validateAndSetConfigValue(newValue);
}}
/>
);
};

export const ConnectorConfigurationField: React.FC<ConnectorConfigurationFieldProps> = ({
configEntry,
isLoading,
@@ -277,30 +196,10 @@ export const ConnectorConfigurationField: React.FC<ConnectorConfigurationFieldPr
setConfigValue(ensureCorrectTyping(configEntry.type, value));
};

const { key, display, sensitive } = configEntry;

switch (display) {
case DisplayType.DROPDOWN:
return (
<ConfigSelectField
key={key}
isLoading={isLoading}
configEntry={configEntry}
validateAndSetConfigValue={validateAndSetConfigValue}
/>
);

case DisplayType.CHECKABLE:
return (
<ConfigCheckableField
key={key}
isLoading={isLoading}
configEntry={configEntry}
validateAndSetConfigValue={validateAndSetConfigValue}
/>
);
const { key, type, sensitive } = configEntry;

case DisplayType.NUMERIC:
switch (type) {
case FieldType.INTEGER:
return (
<ConfigNumberField
key={key}
@@ -310,29 +209,7 @@ export const ConnectorConfigurationField: React.FC<ConnectorConfigurationFieldPr
/>
);

case DisplayType.TEXTAREA:
const textarea = (
<ConfigInputTextArea
key={sensitive ? key + '-sensitive-text-area' : key + 'text-area'}
isLoading={isLoading}
configEntry={configEntry}
validateAndSetConfigValue={validateAndSetConfigValue}
/>
);

return sensitive ? (
<>
<ConfigSensitiveTextArea
isLoading={isLoading}
configEntry={configEntry}
validateAndSetConfigValue={validateAndSetConfigValue}
/>
</>
) : (
textarea
);

case DisplayType.TOGGLE:
case FieldType.BOOLEAN:
return (
<ConfigSwitchField
isLoading={isLoading}
Original file line number Diff line number Diff line change
@@ -12,13 +12,12 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiPanel,
EuiSpacer,
EuiText,
} from '@elastic/eui';

import { i18n } from '@kbn/i18n';
import { ConfigEntryView, DisplayType } from '../../../../common/dynamic_config/types';
import { ConfigEntryView } from '../../../../common/dynamic_config/types';
import { ConnectorConfigurationField } from './connector_configuration_field';

interface ConnectorConfigurationFormItemsProps {
@@ -34,37 +33,24 @@ export const ConnectorConfigurationFormItems: React.FC<ConnectorConfigurationFor
items,
setConfigEntry,
direction,
itemsGrow,
}) => {
return (
<EuiFlexGroup direction={direction} data-test-subj="connector-configuration-fields">
{items.map((configEntry) => {
const {
depends_on: dependencies,
key,
display,
isValid,
label,
sensitive,
tooltip,
validationErrors,
required,
} = configEntry;
const { key, isValid, label, sensitive, description, validationErrors, required } =
configEntry;

const helpText = tooltip;
const helpText = description;
// toggle and sensitive textarea labels go next to the element, not in the row
const rowLabel =
display === DisplayType.TOGGLE || (display === DisplayType.TEXTAREA && sensitive) ? (
<></>
) : tooltip ? (
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem>
<p>{label}</p>
</EuiFlexItem>
</EuiFlexGroup>
) : (
<p>{label}</p>
);
const rowLabel = description ? (
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem>
<p>{label}</p>
</EuiFlexItem>
</EuiFlexGroup>
) : (
<p>{label}</p>
);

const optionalLabel = !required ? (
<EuiText color="subdued" size="xs">
@@ -74,31 +60,6 @@ export const ConnectorConfigurationFormItems: React.FC<ConnectorConfigurationFor
</EuiText>
) : undefined;

if (dependencies?.length > 0) {
return (
<EuiFlexItem key={key} grow={itemsGrow}>
<EuiPanel color="subdued" borderRadius="none">
<EuiFormRow
fullWidth={true}
label={rowLabel}
helpText={helpText}
error={validationErrors}
isInvalid={!isValid}
labelAppend={optionalLabel}
data-test-subj={`connector-configuration-formrow-${key}`}
>
<ConnectorConfigurationField
configEntry={configEntry}
isLoading={isLoading}
setConfigValue={(value) => {
setConfigEntry(configEntry.key, value);
}}
/>
</EuiFormRow>
</EuiPanel>
</EuiFlexItem>
);
}
return (
<EuiFlexItem key={key}>
<EuiFormRow
@@ -114,17 +75,17 @@ export const ConnectorConfigurationFormItems: React.FC<ConnectorConfigurationFor
configEntry={configEntry}
isLoading={isLoading}
setConfigValue={(value) => {
setConfigEntry(configEntry.key, value);
setConfigEntry(key, value);
}}
/>
</EuiFormRow>
{configEntry.sensitive ? (
{sensitive ? (
<>
<EuiSpacer size="s" />
<EuiCallOut
size="s"
color="warning"
title={`You will need to reenter you ${configEntry.label} each time you edit the connector`}
title={`You will need to reenter your ${label} each time you edit the connector`}
/>
</>
) : null}
Original file line number Diff line number Diff line change
@@ -49,7 +49,3 @@ export const ensureIntType = (value: string | number | boolean | null): number |
export const ensureBooleanType = (value: string | number | boolean | null): boolean => {
return Boolean(value);
};

export const hasUiRestrictions = (configEntry: Partial<ConnectorConfigEntry>) => {
return (configEntry.ui_restrictions ?? []).length > 0;
};

Large diffs are not rendered by default.

Large diffs are not rendered by default.

0 comments on commit 246ef2a

Please sign in to comment.