Skip to content

Commit

Permalink
[Onboarding] Add delete index action to search index detail page (ela…
Browse files Browse the repository at this point in the history
…stic#192428)

## Summary

In this PR for Onboarding Search detail page added,
* Delete index action
* Page loading section while fetching index
* Page error section when index not found
* Api reference doc link
* Added FTR tests for the new implementation

## Screenshots
<img width="1462" alt="Screenshot 2024-09-09 at 11 17 05 PM"
src="https://github.com/user-attachments/assets/b407d8ff-36c6-4bd6-a6af-d63d9ee6ac40">

<img width="1484" alt="Screenshot 2024-09-09 at 11 17 33 PM"
src="https://github.com/user-attachments/assets/5ff8a84d-b8fd-4b43-8057-2691a3741353">

**How to test:**
1. Enable searchIndices plugin in `kibana.dev.yml` as this plugin is
behind Feature flag
```
xpack.searchIndices.enabled: true

```
2. Create a new index
3. Navigate to `/app/elasticsearch/indices/index_details/${indexName}`
4. Delete index and visit to
`/app/elasticsearch/indices/index_details/${indexName}` again

### 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)
- [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: kibanamachine <[email protected]>
(cherry picked from commit dcbba95)

# Conflicts:
#	x-pack/plugins/search_indices/tsconfig.json
  • Loading branch information
saarikabhasi committed Sep 16, 2024
1 parent 568f623 commit a15f60f
Show file tree
Hide file tree
Showing 17 changed files with 466 additions and 55 deletions.
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
elasticStackGetStarted: isServerless
? `${SERVERLESS_DOCS}`
: `${ELASTIC_WEBSITE_URL}guide/en/index.html`,
apiReference: `${ELASTIC_WEBSITE_URL}guide/en/starting-with-the-elasticsearch-platform-and-its-solutions/current/api-reference.html`,
upgrade: {
upgradingStackOnPrem: `${ELASTIC_WEBSITE_URL}guide/en/elastic-stack/current/upgrading-elastic-stack-on-prem.html`,
upgradingStackOnCloud: `${ELASTIC_WEBSITE_URL}guide/en/elastic-stack/current/upgrade-elastic-stack-for-elastic-cloud.html`,
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface DocLinksMeta {
export interface DocLinks {
readonly settings: string;
readonly elasticStackGetStarted: string;
readonly apiReference: string;
readonly upgrade: {
readonly upgradingStackOnPrem: string;
readonly upgradingStackOnCloud: string;
Expand Down
19 changes: 19 additions & 0 deletions x-pack/plugins/search_indices/common/doc_links.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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 { DocLinks } from '@kbn/doc-links';

class SearchIndicesDocLinks {
public apiReference: string = '';

constructor() {}

setDocLinks(newDocLinks: DocLinks) {
this.apiReference = newDocLinks.apiReference;
}
}
export const docLinks = new SearchIndicesDocLinks();
1 change: 1 addition & 0 deletions x-pack/plugins/search_indices/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
],
"requiredBundles": [
"kibanaReact",
"esUiShared"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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 React, { Fragment, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiConfirmModal } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { useDeleteIndex } from '../../hooks/api/use_delete_index';
interface DeleteIndexModelProps {
onCancel: () => void;
indexName: string;
navigateToIndexListPage: () => void;
}
export const DeleteIndexModal: React.FC<DeleteIndexModelProps> = ({
onCancel,
indexName,
navigateToIndexListPage,
}) => {
const { mutate, isLoading, isSuccess } = useDeleteIndex(indexName);
useEffect(() => {
if (isSuccess) {
navigateToIndexListPage();
}
}, [navigateToIndexListPage, isSuccess]);
return (
<EuiConfirmModal
data-test-subj="deleteIndexActionModal"
title={i18n.translate(
'xpack.searchIndices.indexActionsMenu.deleteIndex.confirmModal.modalTitle',
{
defaultMessage: 'Delete index',
}
)}
onCancel={onCancel}
onConfirm={() => mutate()}
isLoading={isLoading}
buttonColor="danger"
confirmButtonDisabled={false}
cancelButtonText={i18n.translate(
'xpack.searchIndices.indexActionsMenu.deleteIndex.confirmModal.cancelButtonText',
{
defaultMessage: 'Cancel',
}
)}
confirmButtonText={i18n.translate(
'xpack.searchIndices.indexActionsMenu.deleteIndex.confirmModal.confirmButtonText',
{
defaultMessage: 'Delete index',
}
)}
>
<Fragment>
<p>
<FormattedMessage
id="xpack.searchIndices.indexActionsMenu.deleteIndex.deleteDescription"
defaultMessage="You are about to delete this index:"
/>
</p>
<ul>
<li>{indexName}</li>
</ul>

<p>
<FormattedMessage
id="xpack.searchIndices.indexActionsMenu.deleteIndex.deleteWarningDescription"
defaultMessage="You can't recover a deleted index. Make sure you have appropriate backups."
/>
</p>
</Fragment>
</EuiConfirmModal>
);
};
153 changes: 130 additions & 23 deletions x-pack/plugins/search_indices/public/components/indices/details_page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,98 @@
* 2.0.
*/

import { EuiPageSection, EuiSpacer, EuiButton, EuiPageTemplate } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import {
EuiPageSection,
EuiSpacer,
EuiButton,
EuiPageTemplate,
EuiFlexItem,
EuiFlexGroup,
EuiPopover,
EuiButtonIcon,
EuiContextMenuItem,
EuiContextMenuPanel,
EuiText,
EuiIcon,
EuiButtonEmpty,
} from '@elastic/eui';
import React, { useCallback, 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 { useIndex } from '../../hooks/api/use_index';
import { useKibana } from '../../hooks/use_kibana';
import { DeleteIndexModal } from './delete_index_modal';
import { IndexloadingError } from './details_page_loading_error';

export const SearchIndexDetailsPage = () => {
const indexName = decodeURIComponent(useParams<{ indexName: string }>().indexName);
const { console: consolePlugin, application } = useKibana().services;
const { console: consolePlugin, docLinks, application } = useKibana().services;
const { data: index, refetch, isSuccess, isInitialLoading } = useIndex(indexName);

const { data: index } = useIndex(indexName);
const embeddableConsole = useMemo(
() => (consolePlugin?.EmbeddableConsole ? <consolePlugin.EmbeddableConsole /> : null),
[consolePlugin]
);
const navigateToIndexListPage = useCallback(() => {
application.navigateToApp('management', { deepLinkId: 'index_management' });
}, [application]);

const refetchIndex = useCallback(() => {
refetch();
}, [refetch]);
const [showMoreOptions, setShowMoreOptions] = useState<boolean>(false);
const [isShowingDeleteModal, setShowDeleteIndexModal] = useState<boolean>(false);
const moreOptionsPopover = (
<EuiPopover
isOpen={showMoreOptions}
closePopover={() => setShowMoreOptions(!showMoreOptions)}
button={
<EuiButtonIcon
iconType="boxesVertical"
onClick={() => setShowMoreOptions(!showMoreOptions)}
size="m"
data-test-subj="moreOptionsActionButton"
aria-label={i18n.translate('xpack.searchIndices.moreOptions.ariaLabel', {
defaultMessage: 'More options',
})}
/>
}
>
<EuiContextMenuPanel
data-test-subj="moreOptionsContextMenu"
items={[
<EuiContextMenuItem
key="trash"
icon={<EuiIcon type="trash" color="danger" />}
onClick={() => {
setShowDeleteIndexModal(!isShowingDeleteModal);
}}
size="s"
color="danger"
data-test-subj="moreOptionsDeleteIndex"
>
<EuiText size="s" color="danger">
{i18n.translate('xpack.searchIndices.moreOptions.deleteIndexLabel', {
defaultMessage: 'Delete Index',
})}
</EuiText>
</EuiContextMenuItem>,
]}
/>
</EuiPopover>
);
if (isInitialLoading) {
return (
<SectionLoading>
{i18n.translate('xpack.searchIndices.loadingDescription', {
defaultMessage: 'Loading index details…',
})}
</SectionLoading>
);
}

return (
<EuiPageTemplate
offset={0}
Expand All @@ -32,27 +105,61 @@ export const SearchIndexDetailsPage = () => {
grow={false}
bottomBorder={false}
>
<EuiPageSection>
<EuiButton
data-test-subj="searchIndexDetailsBackToIndicesButton"
color="text"
iconType="arrowLeft"
onClick={navigateToIndexListPage}
>
<FormattedMessage
id="xpack.searchIndices.backToIndicesButtonLabel"
defaultMessage="Back to indices"
{!isSuccess || !index ? (
<IndexloadingError
indexName={indexName}
navigateToIndexListPage={navigateToIndexListPage}
reloadFunction={refetchIndex}
/>
) : (
<>
<EuiPageSection>
<EuiButton
data-test-subj="backToIndicesButton"
color="text"
iconType="arrowLeft"
onClick={() => navigateToIndexListPage()}
>
<FormattedMessage
id="xpack.searchIndices.backToIndicesButtonLabel"
defaultMessage="Back to indices"
/>
</EuiButton>
</EuiPageSection>
<EuiPageTemplate.Header
data-test-subj="searchIndexDetailsHeader"
pageTitle={index?.name}
rightSideItems={[
<EuiFlexGroup>
<EuiFlexItem>
<EuiButtonEmpty
href={docLinks.links.apiReference}
target="_blank"
iconType="documentation"
data-test-subj="ApiReferenceDoc"
>
{i18n.translate('xpack.searchIndices.indexActionsMenu.apiReference.docLink', {
defaultMessage: 'API Reference',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem>{moreOptionsPopover}</EuiFlexItem>
</EuiFlexGroup>,
]}
/>
</EuiButton>
</EuiPageSection>
<EuiPageTemplate.Header
data-test-subj="searchIndexDetailsHeader"
pageTitle={index?.name}
rightSideItems={[]}
/>
<EuiSpacer size="l" />
<EuiSpacer size="l" />

{isShowingDeleteModal && (
<DeleteIndexModal
onCancel={() => setShowDeleteIndexModal(!isShowingDeleteModal)}
indexName={indexName}
navigateToIndexListPage={navigateToIndexListPage}
/>
)}

<div data-test-subj="searchIndexDetailsContent" />
<div data-test-subj="searchIndexDetailsContent" />
</>
)}
{embeddableConsole}
</EuiPageTemplate>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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 {
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiPageTemplate,
EuiText,
} from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
interface IndexloadingErrorProps {
indexName: string;
navigateToIndexListPage: () => void;
reloadFunction: () => void;
}
export const IndexloadingError = ({
indexName,
navigateToIndexListPage,
reloadFunction,
}: IndexloadingErrorProps) => (
<EuiPageTemplate.EmptyPrompt
data-test-subj="pageLoadError"
color="danger"
iconType="warning"
title={
<h2>
<FormattedMessage
id="xpack.searchIndices.pageLoaError.errorTitle"
defaultMessage="Unable to load index details"
/>
</h2>
}
body={
<EuiText color="subdued">
<FormattedMessage
id="xpack.searchIndices.pageLoadError.description"
defaultMessage="We encountered an error loading data for index {indexName}. Make sure that the index name in the URL is correct and try again."
values={{
indexName,
}}
/>
</EuiText>
}
actions={
<EuiFlexGroup justifyContent="spaceAround">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
color="danger"
iconType="arrowLeft"
onClick={() => navigateToIndexListPage()}
data-test-subj="loadingErrorBackToIndicesButton"
>
<FormattedMessage
id="xpack.searchIndices.pageLoadError.backToIndicesButtonLabel"
defaultMessage="Back to indices"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
iconSide="right"
onClick={reloadFunction}
iconType="refresh"
color="danger"
data-test-subj="reloadButton"
>
<FormattedMessage
id="xpack.searchIndices.pageLoadError.reloadButtonLabel"
defaultMessage="Reload"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
}
/>
);
Loading

0 comments on commit a15f60f

Please sign in to comment.