mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
## Summary https://github.com/user-attachments/assets/552e1198-ba95-45d6-b13b-e1b26060d34c This PR adds an option to add a richer design for Result card. Defaults still stay the same with old design. Screenshot from Index Management. <img width="1139" alt="Screenshot 2024-09-27 at 14 52 25" src="https://github.com/user-attachments/assets/754a22c5-c3db-4385-b1ad-4e93a8615b9c"> Added a bunch of options to show score, show amount of fields to show when collapsed by default etc. ### 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) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [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) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
237 lines
7.7 KiB
TypeScript
237 lines
7.7 KiB
TypeScript
/*
|
|
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
* Public License v 1"; you may not use this file except in compliance with, at
|
|
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
|
|
import type { IndicesGetMappingResponse, SearchHit } from '@elastic/elasticsearch/lib/api/types';
|
|
|
|
import {
|
|
EuiButtonEmpty,
|
|
EuiCallOut,
|
|
EuiContextMenuItem,
|
|
EuiContextMenuPanel,
|
|
EuiFlexGroup,
|
|
EuiFlexItem,
|
|
EuiPagination,
|
|
EuiProgress,
|
|
EuiPopover,
|
|
EuiText,
|
|
EuiSpacer,
|
|
Pagination,
|
|
} from '@elastic/eui';
|
|
|
|
import { i18n } from '@kbn/i18n';
|
|
|
|
import { FormattedMessage, FormattedNumber } from '@kbn/i18n-react';
|
|
|
|
import { resultMetaData, resultToFieldFromMappingResponse } from './result/result_metadata';
|
|
|
|
import { Result } from '..';
|
|
import { type ResultProps } from './result/result';
|
|
|
|
interface DocumentListProps {
|
|
dataTelemetryIdPrefix: string;
|
|
docs: SearchHit[];
|
|
docsPerPage: number;
|
|
isLoading: boolean;
|
|
mappings: IndicesGetMappingResponse | undefined;
|
|
meta: Pagination;
|
|
onPaginate: (newPageIndex: number) => void;
|
|
setDocsPerPage?: (docsPerPage: number) => void;
|
|
onDocumentClick?: (doc: SearchHit) => void;
|
|
resultProps?: Partial<ResultProps>;
|
|
}
|
|
|
|
export const DocumentList: React.FC<DocumentListProps> = ({
|
|
dataTelemetryIdPrefix,
|
|
docs,
|
|
docsPerPage,
|
|
isLoading,
|
|
mappings,
|
|
meta,
|
|
onPaginate,
|
|
setDocsPerPage,
|
|
onDocumentClick,
|
|
resultProps = {},
|
|
}) => {
|
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
|
|
const getIconType = (size: number) => {
|
|
return size === docsPerPage ? 'check' : 'empty';
|
|
};
|
|
|
|
const pageCount = meta?.pageSize ? Math.ceil(meta.totalItemCount / meta?.pageSize) : 0;
|
|
return (
|
|
<>
|
|
<EuiPagination
|
|
aria-label={i18n.translate('searchIndexDocuments.documentList.paginationAriaLabel', {
|
|
defaultMessage: 'Pagination for document list',
|
|
})}
|
|
pageCount={pageCount}
|
|
activePage={meta.pageIndex}
|
|
onPageClick={onPaginate}
|
|
/>
|
|
<EuiSpacer size="m" />
|
|
<EuiText size="xs">
|
|
<p>
|
|
<FormattedMessage
|
|
id="searchIndexDocuments.documentList.description"
|
|
defaultMessage="Showing {results} of {total}.
|
|
Search results maxed at {maximum} documents."
|
|
values={{
|
|
maximum: <FormattedNumber value={10000} />,
|
|
results: (
|
|
<strong>
|
|
<FormattedNumber value={docs.length} />
|
|
</strong>
|
|
),
|
|
total: (
|
|
<strong>
|
|
<FormattedNumber value={meta.totalItemCount} />
|
|
</strong>
|
|
),
|
|
}}
|
|
/>
|
|
</p>
|
|
</EuiText>
|
|
{isLoading && <EuiProgress size="xs" color="primary" />}
|
|
<EuiSpacer size="m" />
|
|
{docs.map((doc) => {
|
|
return (
|
|
<React.Fragment key={doc._id}>
|
|
<Result
|
|
fields={resultToFieldFromMappingResponse(doc, mappings)}
|
|
metaData={resultMetaData(doc)}
|
|
onDocumentClick={onDocumentClick ? () => onDocumentClick(doc) : undefined}
|
|
{...resultProps}
|
|
/>
|
|
<EuiSpacer size="s" />
|
|
</React.Fragment>
|
|
);
|
|
})}
|
|
|
|
<EuiFlexGroup justifyContent="spaceBetween">
|
|
<EuiFlexItem grow={false}>
|
|
<EuiPagination
|
|
aria-label={i18n.translate('searchIndexDocuments.documentList.paginationAriaLabel', {
|
|
defaultMessage: 'Pagination for document list',
|
|
})}
|
|
pageCount={pageCount}
|
|
activePage={meta.pageIndex}
|
|
onPageClick={onPaginate}
|
|
/>
|
|
</EuiFlexItem>
|
|
{setDocsPerPage && (
|
|
<EuiFlexItem grow={false}>
|
|
<EuiPopover
|
|
aria-label={i18n.translate('searchIndexDocuments.documentList.docsPerPage', {
|
|
defaultMessage: 'Document count per page dropdown',
|
|
})}
|
|
button={
|
|
<EuiButtonEmpty
|
|
data-telemetry-id={`${dataTelemetryIdPrefix}-documents-docsPerPage`}
|
|
size="s"
|
|
iconType="arrowDown"
|
|
iconSide="right"
|
|
onClick={() => {
|
|
setIsPopoverOpen(true);
|
|
}}
|
|
>
|
|
{i18n.translate('searchIndexDocuments.documentList.pagination.itemsPerPage', {
|
|
defaultMessage: 'Documents per page: {docPerPage}',
|
|
values: { docPerPage: docsPerPage },
|
|
})}
|
|
</EuiButtonEmpty>
|
|
}
|
|
isOpen={isPopoverOpen}
|
|
closePopover={() => {
|
|
setIsPopoverOpen(false);
|
|
}}
|
|
panelPaddingSize="none"
|
|
anchorPosition="downLeft"
|
|
>
|
|
<EuiContextMenuPanel
|
|
size="s"
|
|
items={[
|
|
<EuiContextMenuItem
|
|
key="10 rows"
|
|
icon={getIconType(10)}
|
|
onClick={() => {
|
|
setIsPopoverOpen(false);
|
|
setDocsPerPage(10);
|
|
}}
|
|
>
|
|
{i18n.translate('searchIndexDocuments.documentList.paginationOptions.option', {
|
|
defaultMessage: '{docCount} documents',
|
|
values: { docCount: 10 },
|
|
})}
|
|
</EuiContextMenuItem>,
|
|
|
|
<EuiContextMenuItem
|
|
key="25 rows"
|
|
icon={getIconType(25)}
|
|
onClick={() => {
|
|
setIsPopoverOpen(false);
|
|
setDocsPerPage(25);
|
|
}}
|
|
>
|
|
{i18n.translate('searchIndexDocuments.documentList.paginationOptions.option', {
|
|
defaultMessage: '{docCount} documents',
|
|
values: { docCount: 25 },
|
|
})}
|
|
</EuiContextMenuItem>,
|
|
<EuiContextMenuItem
|
|
key="50 rows"
|
|
icon={getIconType(50)}
|
|
onClick={() => {
|
|
setIsPopoverOpen(false);
|
|
setDocsPerPage(50);
|
|
}}
|
|
>
|
|
{i18n.translate('searchIndexDocuments.documentList.paginationOptions.option', {
|
|
defaultMessage: '{docCount} documents',
|
|
values: { docCount: 50 },
|
|
})}
|
|
</EuiContextMenuItem>,
|
|
]}
|
|
/>
|
|
</EuiPopover>
|
|
</EuiFlexItem>
|
|
)}
|
|
</EuiFlexGroup>
|
|
|
|
<EuiSpacer />
|
|
{meta.totalItemCount > 9999 && (
|
|
<EuiCallOut
|
|
size="s"
|
|
title={
|
|
<FormattedMessage
|
|
id="searchIndexDocuments.documentList.resultLimitTitle"
|
|
defaultMessage="Results are limited to {number} documents"
|
|
values={{
|
|
number: <FormattedNumber value={10000} />,
|
|
}}
|
|
/>
|
|
}
|
|
iconType="search"
|
|
>
|
|
<p>
|
|
<FormattedMessage
|
|
id="searchIndexDocuments.documentList.resultLimit"
|
|
defaultMessage="Only the first {number} results are available for paging. Please use the search bar to filter down your results."
|
|
values={{
|
|
number: <FormattedNumber value={10000} />,
|
|
}}
|
|
/>
|
|
</p>
|
|
</EuiCallOut>
|
|
)}
|
|
</>
|
|
);
|
|
};
|