Skip to content

Commit

Permalink
[Search] Onboarding ux improvements (elastic#196726)
Browse files Browse the repository at this point in the history
Acceptance Criteria

- [x] Update Use in Playground to be a filled button and labelled
“Search in Playground” (not empty button)
- [x] Include View in Discover as a default button in page header
- [x] Hide 'Add reference' in context menu when documents not presented
in index to avoid duplication of this button
- [x] Add Spacer in settings

<img width="1212" alt="image"
src="https://github.com/user-attachments/assets/bcdfca68-b30f-4597-9e80-b0f27fcf6c9b">
<img width="1212" alt="image"
src="https://github.com/user-attachments/assets/48d78874-806f-4ba1-bca6-ba5516e4fcfa">
  • Loading branch information
yansavitski authored Oct 18, 2024
1 parent 2173af7 commit 01d32b0
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import {
EuiTabbedContent,
EuiTabbedContentTab,
useEuiTheme,
EuiButton,
} from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { SectionLoading } from '@kbn/es-ui-shared-plugin/public';
import { ApiKeyForm } from '@kbn/search-api-keys-components';
import { useNavigateToDiscover } from '../../hooks/use_navigate_to_discover';
import { useIndex } from '../../hooks/api/use_index';
import { useKibana } from '../../hooks/use_kibana';
import { ConnectionDetails } from '../connection_details/connection_details';
Expand Down Expand Up @@ -71,13 +73,14 @@ export const SearchIndexDetailsPage = () => {
await playgroundLocator.navigate({ 'default-index': index.name });
}
}, [share, index]);
const navigateToDiscover = useNavigateToDiscover(indexName);

const [isDocumentsExists, setDocumentsExists] = useState<boolean>(false);
const [hasDocuments, setHasDocuments] = useState<boolean>(false);
const [isDocumentsLoading, setDocumentsLoading] = useState<boolean>(true);
useEffect(() => {
setDocumentsLoading(isInitialLoading);
setDocumentsExists(!(!isInitialLoading && indexDocuments?.results?.data.length === 0));
}, [indexDocuments, isInitialLoading, setDocumentsExists, setDocumentsLoading]);
setHasDocuments(!(!isInitialLoading && indexDocuments?.results?.data.length === 0));
}, [indexDocuments, isInitialLoading, setHasDocuments, setDocumentsLoading]);

useEffect(() => {
chrome.docTitle.change(indexName);
Expand Down Expand Up @@ -221,8 +224,37 @@ export const SearchIndexDetailsPage = () => {
bottomBorder={false}
rightSideItems={[
<EuiFlexGroup gutterSize="m">
<EuiFlexItem>
{!isDocumentsExists ? (
{hasDocuments ? (
<>
<EuiFlexItem>
<EuiButtonEmpty
isLoading={isDocumentsLoading}
data-test-subj="viewInDiscoverLink"
onClick={navigateToDiscover}
>
<FormattedMessage
id="xpack.searchIndices.indexAction.useInPlaygroundButtonLabel"
defaultMessage="View in Discover"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton
isLoading={isDocumentsLoading}
data-test-subj="useInPlaygroundLink"
onClick={navigateToPlayground}
iconType="launch"
fill
>
<FormattedMessage
id="xpack.searchIndices.indexAction.useInPlaygroundButtonLabel"
defaultMessage="Search in Playground"
/>
</EuiButton>
</EuiFlexItem>
</>
) : (
<EuiFlexItem>
<EuiButtonEmpty
href={docLinks.links.apiReference}
target="_blank"
Expand All @@ -232,33 +264,15 @@ export const SearchIndexDetailsPage = () => {
>
<FormattedMessage
id="xpack.searchIndices.indexAction.ApiReferenceButtonLabel"
defaultMessage="{buttonLabel}"
values={{
buttonLabel: isDocumentsLoading ? 'Loading' : 'API Reference',
}}
/>
</EuiButtonEmpty>
) : (
<EuiButtonEmpty
isLoading={isDocumentsLoading}
iconType="launch"
data-test-subj="useInPlaygroundLink"
onClick={navigateToPlayground}
>
<FormattedMessage
id="xpack.searchIndices.indexAction.useInPlaygroundButtonLabel"
defaultMessage="{buttonLabel}"
values={{
buttonLabel: isDocumentsLoading ? 'Loading' : 'Use in Playground',
}}
defaultMessage="API Reference"
/>
</EuiButtonEmpty>
)}
</EuiFlexItem>
</EuiFlexItem>
)}
<EuiFlexItem>
<SearchIndexDetailsPageMenuItemPopover
handleDeleteIndexModal={handleDeleteIndexModal}
navigateToPlayground={navigateToPlayground}
showApiReference={hasDocuments}
/>
</EuiFlexItem>
</EuiFlexGroup>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,101 +14,55 @@ import {
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { MouseEventHandler, ReactElement, useState } from 'react';
import React, { ReactElement, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { useKibana } from '../../hooks/use_kibana';

enum MenuItems {
playground = 'playground',
apiReference = 'apiReference',
deleteIndex = 'deleteIndex',
}
interface MenuItemsAction {
href?: string;
onClick?: (() => void) | MouseEventHandler;
}

const SearchIndexDetailsPageMenuItemPopoverItems = [
{
type: MenuItems.playground,
iconType: 'launch',
dataTestSubj: 'moreOptionsPlayground',
iconComponent: <EuiIcon type="launch" />,
target: undefined,
text: (
<EuiText size="s">
{i18n.translate('xpack.searchIndices.moreOptions.playgroundLabel', {
defaultMessage: 'Use in Playground',
})}
</EuiText>
),
color: undefined,
},
{
type: MenuItems.apiReference,
iconType: 'documentation',
dataTestSubj: 'moreOptionsApiReference',
iconComponent: <EuiIcon type="documentation" />,
target: '_blank',
text: (
<EuiText size="s">
{i18n.translate('xpack.searchIndices.moreOptions.apiReferenceLabel', {
defaultMessage: 'API Reference',
})}
</EuiText>
),
color: undefined,
},
{
type: MenuItems.deleteIndex,
iconType: 'trash',
dataTestSubj: 'moreOptionsDeleteIndex',
iconComponent: <EuiIcon color="danger" type="trash" />,
target: undefined,
text: (
<EuiText size="s" color="danger">
{i18n.translate('xpack.searchIndices.moreOptions.deleteIndexLabel', {
defaultMessage: 'Delete Index',
})}
</EuiText>
),
color: 'danger',
},
];
interface SearchIndexDetailsPageMenuItemPopoverProps {
handleDeleteIndexModal: () => void;
navigateToPlayground: () => void;
showApiReference: boolean;
}

export const SearchIndexDetailsPageMenuItemPopover = ({
showApiReference = false,
handleDeleteIndexModal,
navigateToPlayground,
}: SearchIndexDetailsPageMenuItemPopoverProps) => {
const [showMoreOptions, setShowMoreOptions] = useState<boolean>(false);
const { docLinks } = useKibana().services;
const contextMenuItemsActions: Record<MenuItems, MenuItemsAction> = {
playground: {
href: undefined,
onClick: navigateToPlayground,
},
apiReference: { href: docLinks.links.apiReference, onClick: undefined },
deleteIndex: { href: undefined, onClick: handleDeleteIndexModal },
};
const contextMenuItems: ReactElement[] = SearchIndexDetailsPageMenuItemPopoverItems.map(
(item) => (
const contextMenuItems = [
showApiReference && (
<EuiContextMenuItem
key={item.iconType}
icon={item.iconComponent}
href={contextMenuItemsActions[item.type]?.href}
key="apiReference"
icon={<EuiIcon type="documentation" />}
href={docLinks.links.apiReference}
size="s"
onClick={contextMenuItemsActions[item.type]?.onClick}
target={item.target}
data-test-subj={item.dataTestSubj}
color={item.color}
target="_blank"
data-test-subj="moreOptionsApiReference"
>
{item.text}
<EuiText size="s">
<FormattedMessage
id="xpack.searchIndices.moreOptions.apiReferenceLabel"
defaultMessage="API Reference"
/>
</EuiText>
</EuiContextMenuItem>
)
);
),
<EuiContextMenuItem
key="deleteIndex"
icon={<EuiIcon color="danger" type="trash" />}
size="s"
onClick={handleDeleteIndexModal}
data-test-subj="moreOptionsDeleteIndex"
color="danger"
>
<EuiText size="s" color="danger">
<FormattedMessage
id="xpack.searchIndices.moreOptions.deleteIndexLabel"
defaultMessage="Delete Index"
/>
</EuiText>
</EuiContextMenuItem>,
].filter(Boolean) as ReactElement[];

return (
<EuiPopover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import React from 'react';
import { useMemo } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { useKibana } from '../../hooks/use_kibana';

interface SearchIndexDetailsSettingsProps {
Expand All @@ -20,5 +21,10 @@ export const SearchIndexDetailsSettings = ({ indexName }: SearchIndexDetailsSett
[indexManagement, history]
);

return <IndexSettingsComponent indexName={indexName} />;
return (
<>
<EuiSpacer />
<IndexSettingsComponent indexName={indexName} />
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useCallback } from 'react';
import { useKibana } from './use_kibana';

const DISCOVER_LOCATOR_ID = 'DISCOVER_APP_LOCATOR';

export const useNavigateToDiscover = (indexName: string) => {
const { share } = useKibana().services;

return useCallback(async () => {
const discoverLocator = share.url.locators.get(DISCOVER_LOCATOR_ID);
if (discoverLocator && indexName) {
await discoverLocator.navigate({ dataViewSpec: { title: indexName } });
}
}, [share, indexName]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont
async expectAPIReferenceDocLinkExists() {
await testSubjects.existOrFail('ApiReferenceDoc', { timeout: 2000 });
},
async expectUseInPlaygroundLinkExists() {
async expectActionItemReplacedWhenHasDocs() {
await testSubjects.missingOrFail('ApiReferenceDoc', { timeout: 2000 });
await testSubjects.existOrFail('useInPlaygroundLink', { timeout: 5000 });
await testSubjects.existOrFail('viewInDiscoverLink', { timeout: 5000 });
},
async expectConnectionDetails() {
await testSubjects.existOrFail('connectionDetailsEndpoint', { timeout: 2000 });
Expand Down Expand Up @@ -76,9 +78,6 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont
async expectMoreOptionsOverviewMenuIsShown() {
await testSubjects.existOrFail('moreOptionsContextMenu');
},
async expectPlaygroundButtonExistsInMoreOptions() {
await testSubjects.existOrFail('moreOptionsPlayground');
},
async expectToNavigateToPlayground(indexName: string) {
await testSubjects.click('moreOptionsPlayground');
expect(await browser.getCurrentUrl()).contain(
Expand All @@ -89,6 +88,9 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont
async expectAPIReferenceDocLinkExistsInMoreOptions() {
await testSubjects.existOrFail('moreOptionsApiReference', { timeout: 2000 });
},
async expectAPIReferenceDocLinkMissingInMoreOptions() {
await testSubjects.missingOrFail('moreOptionsApiReference', { timeout: 2000 });
},
async expectDeleteIndexButtonExistsInMoreOptions() {
await testSubjects.existOrFail('moreOptionsDeleteIndex');
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader();
await pageObjects.svlSearchIndexDetailPage.expectSearchIndexDetailsTabsExists();
await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExists();
await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkMissingInMoreOptions();
});
it('should have embedded dev console', async () => {
await testHasEmbeddedConsole(pageObjects);
Expand Down Expand Up @@ -117,7 +118,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await svlSearchNavigation.navigateToIndexDetailPage(indexName);
});
it('menu action item should be replaced with playground', async () => {
await pageObjects.svlSearchIndexDetailPage.expectUseInPlaygroundLinkExists();
await pageObjects.svlSearchIndexDetailPage.expectActionItemReplacedWhenHasDocs();
});
it('should have link to API reference doc link in options menu', async () => {
await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton();
await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExistsInMoreOptions();
});
it('should have index documents', async () => {
await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments();
Expand Down Expand Up @@ -167,12 +172,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton();
await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsOverviewMenuIsShown();
});
it('should have link to API reference doc link', async () => {
await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExistsInMoreOptions();
});
it('should have link to playground', async () => {
await pageObjects.svlSearchIndexDetailPage.expectPlaygroundButtonExistsInMoreOptions();
});
it('should delete index', async () => {
await pageObjects.svlSearchIndexDetailPage.expectDeleteIndexButtonExistsInMoreOptions();
await pageObjects.svlSearchIndexDetailPage.clickDeleteIndexButton();
Expand Down

0 comments on commit 01d32b0

Please sign in to comment.