Skip to content

Commit

Permalink
[Search][Onboarding] Start Page File Upload & O11y links (elastic#194231
Browse files Browse the repository at this point in the history
)

## Summary

- Clean-up for start page
  - enabled submitting create index form w/ enter in index name input
- extracted start page example to hook to make it easier to update later
- Moved start page language up so changes are saved when switching
between UI & Code views
- Added File Upload link to the ML file uploader
- Added callouts for O11y  

### Screenshots
<img width="1458" alt="image"
src="https://github.com/user-attachments/assets/3a705ab3-69b7-4f26-83c4-0f4d1642db1b">

### Checklist

- [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)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [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
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed

---------

Co-authored-by: Michael DeFazio <[email protected]>
  • Loading branch information
TattdCodeMonkey and mdefazio authored Sep 30, 2024
1 parent 7730eab commit f7d1dd4
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 177 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/search_indices/common/doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import { DocLinks } from '@kbn/doc-links';
class SearchIndicesDocLinks {
public apiReference: string = '';
public setupSemanticSearch: string = '';
public analyzeLogs: string = '';

constructor() {}

setDocLinks(newDocLinks: DocLinks) {
this.apiReference = newDocLinks.apiReference;
this.setupSemanticSearch = newDocLinks.enterpriseSearch.semanticSearch;
this.analyzeLogs = newDocLinks.serverlessSearch.integrations;
}
}
export const docLinks = new SearchIndicesDocLinks();
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export enum AnalyticsEvents {
startCreateIndexLanguageSelect = 'start_code_lang_select',
startCreateIndexCodeCopyInstall = 'start_code_copy_install',
startCreateIndexCodeCopy = 'start_code_copy',
startFileUploadClick = 'start_file_upload',
indexDetailsInstallCodeCopy = 'index_details_code_copy_install',
indexDetailsAddMappingsCodeCopy = 'index_details_add_mappings_code_copy',
indexDetailsIngestDocumentsCodeCopy = 'index_details_ingest_documents_code_copy',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import { PythonServerlessCreateIndexExamples } from './python';
import { ConsoleCreateIndexExamples } from './sense';

export const DefaultServerlessCodeExamples: CreateIndexCodeExamples = {
exampleType: 'search',
sense: ConsoleCreateIndexExamples.default,
curl: CurlCreateIndexExamples.default,
python: PythonServerlessCreateIndexExamples.default,
javascript: JavascriptServerlessCreateIndexExamples.default,
};

export const DenseVectorSeverlessCodeExamples: CreateIndexCodeExamples = {
exampleType: 'vector',
sense: ConsoleCreateIndexExamples.dense_vector,
curl: CurlCreateIndexExamples.dense_vector,
python: PythonServerlessCreateIndexExamples.dense_vector,
Expand Down
197 changes: 123 additions & 74 deletions x-pack/plugins/search_indices/public/components/start/create_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ import {
EuiFlexItem,
EuiForm,
EuiFormRow,
EuiHorizontalRule,
EuiIcon,
EuiLink,
EuiPanel,
EuiSpacer,
EuiText,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';

import type { UserStartPrivilegesResponse } from '../../../common';
import { AnalyticsEvents } from '../../analytics/constants';
Expand All @@ -28,6 +32,7 @@ import { isValidIndexName } from '../../utils/indices';
import { useCreateIndex } from './hooks/use_create_index';

import { CreateIndexFormState } from './types';
import { useKibana } from '../../hooks/use_kibana';

const CREATE_INDEX_CONTENT = i18n.translate(
'xpack.searchIndices.startPage.createIndex.action.text',
Expand All @@ -47,6 +52,7 @@ export const CreateIndexForm = ({
formState,
setFormState,
}: CreateIndexFormProps) => {
const { application } = useKibana().services;
const [indexNameHasError, setIndexNameHasError] = useState<boolean>(false);
const usageTracker = useUsageTracker();
const { createIndex, isLoading } = useCreateIndex();
Expand All @@ -65,93 +71,136 @@ export const CreateIndexForm = ({
setIndexNameHasError(invalidIndexName);
}
};
const onFileUpload = useCallback(() => {
usageTracker.click(AnalyticsEvents.startFileUploadClick);
application.navigateToApp('ml', { path: 'filedatavisualizer' });
}, [usageTracker, application]);

return (
<EuiForm data-test-subj="createIndexUIView" fullWidth>
<EuiFormRow
label={i18n.translate('xpack.searchIndices.startPage.createIndex.name.label', {
defaultMessage: 'Name your index',
})}
helpText={i18n.translate('xpack.searchIndices.startPage.createIndex.name.helpText', {
defaultMessage: 'Index names must be lowercase and can only contain hyphens and numbers',
})}
fullWidth
isInvalid={indexNameHasError}
>
<EuiFieldText
autoFocus
<>
<EuiForm data-test-subj="createIndexUIView" fullWidth component="form">
<EuiFormRow
label={i18n.translate('xpack.searchIndices.startPage.createIndex.name.label', {
defaultMessage: 'Name your index',
})}
helpText={i18n.translate('xpack.searchIndices.startPage.createIndex.name.helpText', {
defaultMessage:
'Index names must be lowercase and can only contain hyphens and numbers',
})}
fullWidth
data-test-subj="indexNameField"
name="indexName"
value={formState.indexName}
isInvalid={indexNameHasError}
disabled={userPrivileges?.privileges?.canCreateIndex === false}
onChange={onIndexNameChange}
placeholder={i18n.translate(
'xpack.searchIndices.startPage.createIndex.name.placeholder',
{
defaultMessage: 'Enter a name for your index',
}
)}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
{userPrivileges?.privileges?.canCreateIndex === false ? (
<EuiToolTip
content={
<p>
{i18n.translate('xpack.searchIndices.startPage.createIndex.permissionTooltip', {
defaultMessage: 'You do not have permission to create an index.',
})}
</p>
>
<EuiFieldText
autoFocus
fullWidth
data-test-subj="indexNameField"
name="indexName"
value={formState.indexName}
isInvalid={indexNameHasError}
disabled={userPrivileges?.privileges?.canCreateIndex === false}
onChange={onIndexNameChange}
placeholder={i18n.translate(
'xpack.searchIndices.startPage.createIndex.name.placeholder',
{
defaultMessage: 'Enter a name for your index',
}
>
)}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
{userPrivileges?.privileges?.canCreateIndex === false ? (
<EuiToolTip
content={
<p>
{i18n.translate('xpack.searchIndices.startPage.createIndex.permissionTooltip', {
defaultMessage: 'You do not have permission to create an index.',
})}
</p>
}
>
<EuiButton
fill
color="primary"
iconSide="left"
iconType="sparkles"
data-test-subj="createIndexBtn"
disabled={true}
>
{CREATE_INDEX_CONTENT}
</EuiButton>
</EuiToolTip>
) : (
<EuiButton
fill
color="primary"
iconSide="left"
iconType="sparkles"
data-telemetry-id="searchIndices-start-createIndexBtn"
data-test-subj="createIndexBtn"
disabled={true}
disabled={indexNameHasError || isLoading}
isLoading={isLoading}
onClick={onCreateIndex}
type="submit"
>
{CREATE_INDEX_CONTENT}
</EuiButton>
</EuiToolTip>
) : (
<EuiButton
fill
color="primary"
iconSide="left"
iconType="sparkles"
data-test-subj="createIndexBtn"
disabled={indexNameHasError || isLoading}
isLoading={isLoading}
onClick={onCreateIndex}
>
{CREATE_INDEX_CONTENT}
</EuiButton>
)}
</EuiFlexItem>
<EuiFlexItem>
{userPrivileges?.privileges?.canCreateApiKeys && (
<EuiFlexGroup gutterSize="s">
<EuiIcon size="l" type="key" color="subdued" />
<EuiText size="s" data-test-subj="apiKeyLabel">
<p>
{i18n.translate(
'xpack.searchIndices.startPage.createIndex.apiKeyCreation.description',
{
defaultMessage: "We'll create an API key for this index",
}
)}
</p>
</EuiText>
</EuiFlexGroup>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiForm>
)}
</EuiFlexItem>
<EuiFlexItem>
{userPrivileges?.privileges?.canCreateApiKeys && (
<EuiFlexGroup gutterSize="s">
<EuiIcon size="m" type="key" color="subdued" />
<EuiText size="s" data-test-subj="apiKeyLabel">
<p>
{i18n.translate(
'xpack.searchIndices.startPage.createIndex.apiKeyCreation.description',
{
defaultMessage: "We'll create an API key for this index",
}
)}
</p>
</EuiText>
</EuiFlexGroup>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiForm>
<EuiHorizontalRule margin="none" />
<EuiPanel color="transparent" paddingSize="s">
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type="documents" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText color="subdued" size="s">
<p>
<FormattedMessage
id="xpack.searchIndices.startPage.createIndex.fileUpload.text"
defaultMessage="Already have some data? {link}"
values={{
link: (
<EuiLink
data-telemetry-id="searchIndices-start-uploadFile"
data-test-subj="uploadFileLink"
onClick={onFileUpload}
>
{i18n.translate(
'xpack.searchIndices.startPage.createIndex.fileUpload.link',
{
defaultMessage: 'Upload a file',
}
)}
</EuiLink>
),
}}
/>
</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,47 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useMemo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { TryInConsoleButton } from '@kbn/try-in-console';

import { AnalyticsEvents } from '../../analytics/constants';
import { Languages, AvailableLanguages, LanguageOptions } from '../../code_examples';
import { DenseVectorSeverlessCodeExamples } from '../../code_examples/create_index';

import { useUsageTracker } from '../../hooks/use_usage_tracker';
import { useKibana } from '../../hooks/use_kibana';
import { useElasticsearchUrl } from '../../hooks/use_elasticsearch_url';
import { getDefaultCodingLanguage } from '../../utils/language';

import { CodeSample } from '../shared/code_sample';
import { LanguageSelector } from '../shared/language_selector';

import { CreateIndexFormState } from './types';
import { useStartPageCodingExamples } from './hooks/use_coding_examples';

export interface CreateIndexCodeViewProps {
createIndexForm: CreateIndexFormState;
changeCodingLanguage: (language: AvailableLanguages) => void;
}

// TODO: this will be dynamic based on stack / es3 & onboarding token
const SelectedCodeExamples = DenseVectorSeverlessCodeExamples;

export const CreateIndexCodeView = ({ createIndexForm }: CreateIndexCodeViewProps) => {
export const CreateIndexCodeView = ({
createIndexForm,
changeCodingLanguage,
}: CreateIndexCodeViewProps) => {
const { application, share, console: consolePlugin } = useKibana().services;
const usageTracker = useUsageTracker();
const selectedCodeExamples = useStartPageCodingExamples();

const [selectedLanguage, setSelectedLanguage] =
useState<AvailableLanguages>(getDefaultCodingLanguage);
const { codingLanguage: selectedLanguage } = createIndexForm;
const onSelectLanguage = useCallback(
(value: AvailableLanguages) => {
setSelectedLanguage(value);
changeCodingLanguage(value);
usageTracker.count([
AnalyticsEvents.startCreateIndexLanguageSelect,
`${AnalyticsEvents.startCreateIndexLanguageSelect}_${value}`,
]);
},
[usageTracker]
[usageTracker, changeCodingLanguage]
);
const elasticsearchUrl = useElasticsearchUrl();
const codeParams = useMemo(() => {
Expand All @@ -53,8 +54,8 @@ export const CreateIndexCodeView = ({ createIndexForm }: CreateIndexCodeViewProp
};
}, [createIndexForm.indexName, elasticsearchUrl]);
const selectedCodeExample = useMemo(() => {
return SelectedCodeExamples[selectedLanguage];
}, [selectedLanguage]);
return selectedCodeExamples[selectedLanguage];
}, [selectedLanguage, selectedCodeExamples]);

return (
<EuiFlexGroup direction="column" data-test-subj="createIndexCodeView">
Expand All @@ -69,7 +70,7 @@ export const CreateIndexCodeView = ({ createIndexForm }: CreateIndexCodeViewProp
{selectedLanguage === 'curl' && (
<EuiFlexItem grow={false}>
<TryInConsoleButton
request={SelectedCodeExamples.sense.createIndex(codeParams)}
request={selectedCodeExamples.sense.createIndex(codeParams)}
application={application}
sharePlugin={share}
consolePlugin={consolePlugin}
Expand Down Expand Up @@ -102,8 +103,7 @@ export const CreateIndexCodeView = ({ createIndexForm }: CreateIndexCodeViewProp
usageTracker.click([
AnalyticsEvents.startCreateIndexCodeCopy,
`${AnalyticsEvents.startCreateIndexCodeCopy}_${selectedLanguage}`,
// TODO: vector should be a parameter when have multiple options
`${AnalyticsEvents.startCreateIndexCodeCopy}_${selectedLanguage}_vector`,
`${AnalyticsEvents.startCreateIndexCodeCopy}_${selectedLanguage}_${selectedCodeExamples.exampleType}`,
]);
}}
/>
Expand Down
Loading

0 comments on commit f7d1dd4

Please sign in to comment.