Skip to content

Commit bb7e71b

Browse files
authored
[Search] Add missing name and description prompt (#177319)
## Summary https://github.com/elastic/kibana/assets/1410658/0e93c5c8-4c01-44ba-b230-bff4ad87aa63 Add name and description prompt to the connector view. This is an important feature that was missing, we cannot ship without this. ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
1 parent f21bee4 commit bb7e71b

6 files changed

Lines changed: 221 additions & 17 deletions

File tree

x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/update_connector_name_and_description_api_logic.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,21 @@ export type PutConnectorNameAndDescriptionArgs = Partial<
1616
Pick<Connector, 'name' | 'description'>
1717
> & {
1818
connectorId: string;
19-
indexName: string;
2019
};
2120

22-
export type PutConnectorNameAndDescriptionResponse = Pick<Connector, 'name' | 'description'> & {
23-
indexName: string;
24-
};
21+
export type PutConnectorNameAndDescriptionResponse = Pick<Connector, 'name' | 'description'>;
2522

2623
export const putConnectorNameAndDescription = async ({
2724
connectorId,
2825
description = null,
29-
indexName,
3026
name = '',
3127
}: PutConnectorNameAndDescriptionArgs) => {
3228
const route = `/internal/enterprise_search/connectors/${connectorId}/name_and_description`;
3329

3430
await HttpLogic.values.http.put(route, {
3531
body: JSON.stringify({ description, name }),
3632
});
37-
return { description, indexName, name };
33+
return { description, name };
3834
};
3935

4036
export const ConnectorNameAndDescriptionApiLogic = createApiLogic(

x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_detail.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { SearchIndexIndexMappings } from '../search_index/index_mappings';
2626
import { SearchIndexPipelines } from '../search_index/pipelines/pipelines';
2727

2828
import { ConnectorConfiguration } from './connector_configuration';
29+
import { ConnectorNameAndDescription } from './connector_name_and_description';
2930
import { ConnectorViewLogic } from './connector_view_logic';
3031
import { ConnectorDetailOverview } from './overview';
3132

@@ -225,7 +226,7 @@ export const ConnectorDetail: React.FC = () => {
225226
pageViewTelemetry={tabId}
226227
isLoading={isLoading}
227228
pageHeader={{
228-
pageTitle: connector?.name ?? '...',
229+
pageTitle: connector ? <ConnectorNameAndDescription connector={connector} /> : '...',
229230
rightSideItems: getHeaderActions(index, hasAppSearchAccess),
230231
tabs,
231232
}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import React, { ChangeEvent, useEffect, useState } from 'react';
9+
10+
import { useActions, useValues } from 'kea';
11+
12+
import { EuiFlexGroup, EuiFlexItem, EuiInlineEditText, EuiInlineEditTitle } from '@elastic/eui';
13+
14+
import { i18n } from '@kbn/i18n';
15+
import { Connector } from '@kbn/search-connectors';
16+
17+
import { ConnectorNameAndDescriptionLogic } from './connector_name_and_description_logic';
18+
19+
export interface ConnectorNameAndDescriptionProps {
20+
connector: Connector;
21+
}
22+
23+
export interface ResolverObject {
24+
rej: (value: boolean | void | PromiseLike<boolean | void>) => void;
25+
res: (value: boolean | void | PromiseLike<boolean | void>) => void;
26+
}
27+
28+
let promise: Promise<boolean | void> | undefined;
29+
30+
const getValidationPromiseResolvers = (): ResolverObject => {
31+
const resolvers = {
32+
rej: () => {},
33+
res: () => {},
34+
};
35+
promise = new Promise((resolve, reject) => {
36+
resolvers.res = resolve;
37+
resolvers.rej = reject;
38+
});
39+
return resolvers;
40+
};
41+
42+
export const ConnectorNameAndDescription: React.FC<ConnectorNameAndDescriptionProps> = ({
43+
connector,
44+
}) => {
45+
const [resolverObject, setResolverObject] = useState<ResolverObject>({
46+
rej: () => {},
47+
res: () => {},
48+
});
49+
const [connectorName, setConnectorName] = useState<string>(connector.name);
50+
const [connectorDescription, setConnectorDescription] = useState<string | null>(
51+
connector.description
52+
);
53+
const [nameErrors, setNameErrors] = useState<string[]>([]);
54+
const { saveNameAndDescription, setConnector } = useActions(ConnectorNameAndDescriptionLogic);
55+
const { status, isLoading, isFailed, isSuccess } = useValues(ConnectorNameAndDescriptionLogic);
56+
useEffect(() => {
57+
setConnector(connector);
58+
}, [connector]);
59+
60+
useEffect(() => {
61+
if (isSuccess) {
62+
resolverObject.res(true);
63+
}
64+
if (isFailed) {
65+
resolverObject.rej();
66+
}
67+
}, [status]);
68+
69+
return (
70+
<EuiFlexGroup direction="column" gutterSize="xs">
71+
<EuiFlexItem grow={false}>
72+
<EuiInlineEditTitle
73+
heading="h1"
74+
inputAriaLabel={i18n.translate(
75+
'xpack.enterpriseSearch.content.connectors.nameAndDescription.name.ariaLabel',
76+
{ defaultMessage: 'Edit connector name' }
77+
)}
78+
placeholder={i18n.translate(
79+
'xpack.enterpriseSearch.content.connectors.nameAndDescription.name.placeholder',
80+
{ defaultMessage: 'Add a name to your connector' }
81+
)}
82+
value={connectorName}
83+
isLoading={isLoading}
84+
isInvalid={nameErrors.length > 0}
85+
size="m"
86+
editModeProps={{
87+
formRowProps: { error: nameErrors },
88+
cancelButtonProps: { onClick: () => setNameErrors([]) },
89+
inputProps: { readOnly: isLoading },
90+
}}
91+
onSave={(inputValue) => {
92+
if (inputValue.trim().length <= 0) {
93+
setNameErrors([
94+
i18n.translate(
95+
'xpack.enterpriseSearch.content.nameAndDescription.name.error.empty',
96+
{ defaultMessage: 'Connector name cannot be empty' }
97+
),
98+
]);
99+
return false;
100+
}
101+
setConnectorName(inputValue);
102+
saveNameAndDescription({ description: connectorDescription, name: inputValue });
103+
setResolverObject(getValidationPromiseResolvers());
104+
return promise;
105+
}}
106+
onChange={(event: ChangeEvent<HTMLInputElement>) => {
107+
setConnectorName(event.target.value);
108+
}}
109+
onCancel={(previousValue) => {
110+
setConnectorName(previousValue);
111+
}}
112+
/>
113+
</EuiFlexItem>
114+
<EuiFlexItem grow={false}>
115+
<EuiInlineEditText
116+
isLoading={isLoading}
117+
inputAriaLabel={i18n.translate(
118+
'xpack.enterpriseSearch.content.connectors.nameAndDescription.description.ariaLabel',
119+
{ defaultMessage: 'Edit connector description' }
120+
)}
121+
placeholder={i18n.translate(
122+
'xpack.enterpriseSearch.content.connectors.nameAndDescription.description.placeholder',
123+
{ defaultMessage: 'Add a description' }
124+
)}
125+
value={connectorDescription || ''}
126+
onSave={(inputValue) => {
127+
setConnectorDescription(inputValue);
128+
saveNameAndDescription({ description: inputValue, name: connectorName });
129+
setResolverObject(getValidationPromiseResolvers());
130+
return promise;
131+
}}
132+
onChange={(event: ChangeEvent<HTMLInputElement>) => {
133+
setConnectorDescription(event.target.value);
134+
}}
135+
onCancel={(previousValue) => {
136+
setConnectorDescription(previousValue);
137+
}}
138+
/>
139+
</EuiFlexItem>
140+
</EuiFlexGroup>
141+
);
142+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { kea, MakeLogicType } from 'kea';
9+
10+
import { Connector } from '@kbn/search-connectors';
11+
12+
import { Status } from '../../../../../common/types/api';
13+
14+
import { Actions } from '../../../shared/api_logic/create_api_logic';
15+
import {
16+
ConnectorNameAndDescriptionApiLogic,
17+
PutConnectorNameAndDescriptionArgs,
18+
PutConnectorNameAndDescriptionResponse,
19+
} from '../../api/connector/update_connector_name_and_description_api_logic';
20+
21+
type NameAndDescription = Partial<Pick<Connector, 'name' | 'description'>>;
22+
23+
type ConnectorNameAndDescriptionActions = Pick<
24+
Actions<PutConnectorNameAndDescriptionArgs, PutConnectorNameAndDescriptionResponse>,
25+
'makeRequest'
26+
> & {
27+
saveNameAndDescription(nameAndDescription: NameAndDescription): NameAndDescription;
28+
setConnector(connector: Connector): Connector;
29+
};
30+
31+
interface ConnectorNameAndDescriptionValues {
32+
connector: Connector | null;
33+
isFailed: boolean;
34+
isLoading: boolean;
35+
isSuccess: boolean;
36+
status: Status;
37+
}
38+
39+
export const ConnectorNameAndDescriptionLogic = kea<
40+
MakeLogicType<ConnectorNameAndDescriptionValues, ConnectorNameAndDescriptionActions>
41+
>({
42+
actions: {
43+
saveNameAndDescription: (nameAndDescription) => nameAndDescription,
44+
setConnector: (connector) => connector,
45+
},
46+
connect: {
47+
actions: [ConnectorNameAndDescriptionApiLogic, ['makeRequest']],
48+
values: [ConnectorNameAndDescriptionApiLogic, ['status']],
49+
},
50+
listeners: ({ actions, values }) => ({
51+
saveNameAndDescription: ({ name, description }) => {
52+
if (values.connector) {
53+
actions.makeRequest({
54+
connectorId: values.connector.id,
55+
description,
56+
name,
57+
});
58+
}
59+
},
60+
}),
61+
path: ['enterprise_search', 'content', 'connector', 'name_and_description'],
62+
reducers: () => ({
63+
connector: [
64+
null,
65+
{
66+
setConnector: (_, connector) => connector,
67+
},
68+
],
69+
}),
70+
selectors: ({ selectors }) => ({
71+
isFailed: [() => [selectors.status], (status: Status) => status === Status.ERROR],
72+
isLoading: [() => [selectors.status], (status: Status) => status === Status.LOADING],
73+
isSuccess: [() => [selectors.status], (status: Status) => status === Status.SUCCESS],
74+
}),
75+
});

x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,6 @@ export const ConnectorViewLogic = kea<MakeLogicType<ConnectorViewValues, Connect
101101
},
102102
}),
103103
path: ['enterprise_search', 'content', 'connector_view_logic'],
104-
reducers: {
105-
syncTriggeredLocally: [
106-
false,
107-
{
108-
fetchIndexApiSuccess: () => false,
109-
startSyncApiSuccess: () => true,
110-
},
111-
],
112-
},
113104
selectors: ({ selectors }) => ({
114105
connector: [
115106
() => [selectors.connectorData],

x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_name_and_description/connector_name_and_description_logic.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ export const ConnectorNameAndDescriptionLogic = kea<
8080
if (isConnectorIndex(values.index) || isCrawlerIndex(values.index)) {
8181
actions.makeRequest({
8282
connectorId: values.index.connector.id,
83-
indexName: values.index.connector.index_name ?? '',
8483
...values.localNameAndDescription,
8584
});
8685
}

0 commit comments

Comments
 (0)