From 3673019906784679af728c47dd27a1bf707bc3e9 Mon Sep 17 00:00:00 2001 From: Constance Date: Mon, 21 Jun 2021 14:37:24 -0700 Subject: [PATCH] [App Search] Convert Documents views to new page template + minor UI polish (#102807) * Convert Documents view to new page template * [UI polish] Move empty state to top-level instead of showing full UI - per Davey's previous approval * [UX polish] Show loading indicator on initial documents page load * Convert single Document detail view to new page template * Update router --- .../documents/components/empty_state.tsx | 66 +++++++++---------- .../documents/document_detail.test.tsx | 21 ++---- .../components/documents/document_detail.tsx | 44 +++++-------- .../components/documents/documents.test.tsx | 14 ++-- .../components/documents/documents.tsx | 33 +++++----- .../search_experience/search_experience.scss | 1 + .../search_experience.test.tsx | 10 --- .../search_experience/search_experience.tsx | 7 +- .../search_experience_content.test.tsx | 5 +- .../search_experience_content.tsx | 3 +- .../components/engine/engine_router.tsx | 20 +++--- 11 files changed, 90 insertions(+), 134 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx index 0f9455a3b9228c..39fe02a84854cf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/components/empty_state.tsx @@ -7,43 +7,41 @@ import React from 'react'; -import { EuiButton, EuiEmptyPrompt, EuiPanel } from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DOCS_PREFIX } from '../../../routes'; export const EmptyState = () => ( - - - {i18n.translate('xpack.enterpriseSearch.appSearch.documents.empty.title', { - defaultMessage: 'Add your first documents', - })} - - } - body={ -

- {i18n.translate('xpack.enterpriseSearch.appSearch.documents.empty.description', { - defaultMessage: - 'You can index documents using the App Search Web Crawler, by uploading JSON, or by using the API.', - })} -

- } - actions={ - - {i18n.translate('xpack.enterpriseSearch.appSearch.engine.documents.empty.buttonLabel', { - defaultMessage: 'Read the documents guide', - })} - - } - /> -
+ + {i18n.translate('xpack.enterpriseSearch.appSearch.documents.empty.title', { + defaultMessage: 'Add your first documents', + })} + + } + body={ +

+ {i18n.translate('xpack.enterpriseSearch.appSearch.documents.empty.description', { + defaultMessage: + 'You can index documents using the App Search Web Crawler, by uploading JSON, or by using the API.', + })} +

+ } + actions={ + + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.documents.empty.buttonLabel', { + defaultMessage: 'Read the documents guide', + })} + + } + /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.test.tsx index 4aade8e61b0851..90da5bebe6d230 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.test.tsx @@ -14,9 +14,10 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiPageHeader, EuiPageContent, EuiBasicTable } from '@elastic/eui'; +import { EuiPanel, EuiBasicTable } from '@elastic/eui'; + +import { getPageHeaderActions } from '../../../test_helpers'; -import { Loading } from '../../../shared/loading'; import { ResultFieldValue } from '../result'; import { DocumentDetail } from '.'; @@ -45,7 +46,7 @@ describe('DocumentDetail', () => { it('renders', () => { const wrapper = shallow(); - expect(wrapper.find(EuiPageContent).length).toBe(1); + expect(wrapper.find(EuiPanel).length).toBe(1); }); it('initializes data on mount', () => { @@ -59,17 +60,6 @@ describe('DocumentDetail', () => { expect(actions.setFields).toHaveBeenCalledWith([]); }); - it('will show a loader while data is loading', () => { - setMockValues({ - ...values, - dataLoading: true, - }); - - const wrapper = shallow(); - - expect(wrapper.find(Loading).length).toBe(1); - }); - describe('field values list', () => { let columns: any; @@ -102,8 +92,7 @@ describe('DocumentDetail', () => { it('will delete the document when the delete button is pressed', () => { const wrapper = shallow(); - const header = wrapper.find(EuiPageHeader).dive().children().dive(); - const button = header.find('[data-test-subj="DeleteDocumentButton"]'); + const button = getPageHeaderActions(wrapper).find('[data-test-subj="DeleteDocumentButton"]'); button.simulate('click'); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.tsx index 314c3529cf4db7..175fb1239d3802 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail.tsx @@ -10,22 +10,13 @@ import { useParams } from 'react-router-dom'; import { useActions, useValues } from 'kea'; -import { - EuiButton, - EuiPageHeader, - EuiPageContentBody, - EuiPageContent, - EuiBasicTable, - EuiBasicTableColumn, -} from '@elastic/eui'; +import { EuiPanel, EuiButton, EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DELETE_BUTTON_LABEL } from '../../../shared/constants'; -import { FlashMessages } from '../../../shared/flash_messages'; -import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; -import { Loading } from '../../../shared/loading'; import { useDecodedParams } from '../../utils/encode_path_params'; import { getEngineBreadcrumbs } from '../engine'; +import { AppSearchPageTemplate } from '../layout'; import { ResultFieldValue } from '../result'; import { DOCUMENTS_TITLE } from './constants'; @@ -52,10 +43,6 @@ export const DocumentDetail: React.FC = () => { }; }, []); - if (dataLoading) { - return ; - } - const columns: Array> = [ { name: i18n.translate('xpack.enterpriseSearch.appSearch.documentDetail.fieldHeader', { @@ -74,11 +61,11 @@ export const DocumentDetail: React.FC = () => { ]; return ( - <> - - { > {DELETE_BUTTON_LABEL} , - ]} - /> - - - - - - - + ], + }} + isLoading={dataLoading} + > + + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.test.tsx index 143ad3f55ff2fb..b5b6dd453c9df1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.test.tsx @@ -10,9 +10,9 @@ import '../../__mocks__/engine_logic.mock'; import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; +import { shallow } from 'enzyme'; -import { EuiPageHeader } from '@elastic/eui'; +import { getPageHeaderActions } from '../../../test_helpers'; import { DocumentCreationButton } from './components'; import { SearchExperience } from './search_experience'; @@ -22,6 +22,7 @@ import { Documents } from '.'; describe('Documents', () => { const values = { isMetaEngine: false, + engine: { document_count: 1 }, myRole: { canManageEngineDocuments: true }, }; @@ -36,9 +37,6 @@ describe('Documents', () => { }); describe('DocumentCreationButton', () => { - const getHeader = (wrapper: ShallowWrapper) => - wrapper.find(EuiPageHeader).dive().children().dive(); - it('renders a DocumentCreationButton if the user can manage engine documents', () => { setMockValues({ ...values, @@ -46,7 +44,7 @@ describe('Documents', () => { }); const wrapper = shallow(); - expect(getHeader(wrapper).find(DocumentCreationButton).exists()).toBe(true); + expect(getPageHeaderActions(wrapper).find(DocumentCreationButton).exists()).toBe(true); }); it('does not render a DocumentCreationButton if the user cannot manage engine documents', () => { @@ -56,7 +54,7 @@ describe('Documents', () => { }); const wrapper = shallow(); - expect(getHeader(wrapper).find(DocumentCreationButton).exists()).toBe(false); + expect(getPageHeaderActions(wrapper).find(DocumentCreationButton).exists()).toBe(false); }); it('does not render a DocumentCreationButton for meta engines even if the user can manage engine documents', () => { @@ -67,7 +65,7 @@ describe('Documents', () => { }); const wrapper = shallow(); - expect(getHeader(wrapper).find(DocumentCreationButton).exists()).toBe(false); + expect(getPageHeaderActions(wrapper).find(DocumentCreationButton).exists()).toBe(false); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx index b4122a715f9270..62c7759757bda8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx @@ -9,35 +9,32 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiPageHeader, EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FlashMessages } from '../../../shared/flash_messages'; -import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; - import { AppLogic } from '../../app_logic'; import { EngineLogic, getEngineBreadcrumbs } from '../engine'; +import { AppSearchPageTemplate } from '../layout'; -import { DocumentCreationButton } from './components'; +import { DocumentCreationButton, EmptyState } from './components'; import { DOCUMENTS_TITLE } from './constants'; import { SearchExperience } from './search_experience'; export const Documents: React.FC = () => { - const { isMetaEngine } = useValues(EngineLogic); + const { isMetaEngine, engine } = useValues(EngineLogic); const { myRole } = useValues(AppLogic); return ( - <> - - ] - : undefined - } - /> - + ] : [], + }} + isEmptyState={!engine.document_count} + emptyState={} + > {isMetaEngine && ( <> { )} - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss index d2e0a8155fa557..34aac402fbb39a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss @@ -15,6 +15,7 @@ .documentsSearchExperience__content { flex-grow: 4; + position: relative; } .documentsSearchExperience__pagingInfo { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx index a4d1a92ee45a4f..3e8a9c1ab307c0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx @@ -20,8 +20,6 @@ jest.mock('../../../../shared/use_local_storage', () => ({ })); import { useLocalStorage } from '../../../../shared/use_local_storage'; -import { EmptyState } from '../components'; - import { CustomizationCallout } from './customization_callout'; import { CustomizationModal } from './customization_modal'; import { SearchExperienceContent } from './search_experience_content'; @@ -58,14 +56,6 @@ describe('SearchExperience', () => { expect(wrapper.find(SearchExperienceContent)).toHaveLength(1); }); - it('renders an empty state when the engine does not have documents', () => { - setMockValues({ ...values, engine: { ...values.engine, document_count: 0 } }); - const wrapper = shallow(); - - expect(wrapper.find(EmptyState)).toHaveLength(1); - expect(wrapper.find(SearchExperienceContent)).toHaveLength(0); - }); - describe('when there are no selected filter fields', () => { let wrapper: ShallowWrapper; beforeEach(() => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx index 22029956601a65..709dfc69905f0a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx @@ -21,7 +21,6 @@ import './search_experience.scss'; import { externalUrl } from '../../../../shared/enterprise_search_url'; import { useLocalStorage } from '../../../../shared/use_local_storage'; import { EngineLogic } from '../../engine'; -import { EmptyState } from '../components'; import { buildSearchUIConfig } from './build_search_ui_config'; import { buildSortOptions } from './build_sort_options'; @@ -141,11 +140,7 @@ export const SearchExperience: React.FC = () => { )} - {engine.document_count && engine.document_count > 0 ? ( - - ) : ( - - )} + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx index 44a6da51ec8d68..e573502d76b9fb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx @@ -15,6 +15,7 @@ import { shallow } from 'enzyme'; // @ts-expect-error types are not available for this package yet import { Results } from '@elastic/react-search-ui'; +import { Loading } from '../../../../shared/loading'; import { SchemaType } from '../../../../shared/schema/types'; import { Pagination } from './pagination'; @@ -82,13 +83,13 @@ describe('SearchExperienceContent', () => { expect(wrapper.find(Pagination).exists()).toBe(true); }); - it('renders empty if a search was not performed yet', () => { + it('renders a loading state if a search was not performed yet', () => { setMockSearchContextState({ ...searchState, wasSearched: false, }); const wrapper = shallow(); - expect(wrapper.isEmptyRender()).toBe(true); + expect(wrapper.find(Loading)).toHaveLength(1); }); it('renders results if a search was performed and there are more than 0 totalResults', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx index 84fe721f9eb7f0..2322bcde831eba 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx @@ -14,6 +14,7 @@ import { EuiFlexGroup, EuiSpacer, EuiEmptyPrompt } from '@elastic/eui'; import { Results, Paging, ResultsPerPage } from '@elastic/react-search-ui'; import { i18n } from '@kbn/i18n'; +import { Loading } from '../../../../shared/loading'; import { EngineLogic } from '../../engine'; import { Result } from '../../result/types'; @@ -26,7 +27,7 @@ export const SearchExperienceContent: React.FC = () => { const { isMetaEngine, engine } = useValues(EngineLogic); - if (!wasSearched) return null; + if (!wasSearched) return ; if (totalResults) { return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx index 6510e99a000fc5..98627950016fb4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx @@ -94,6 +94,16 @@ export const EngineRouter: React.FC = () => { + {canViewEngineDocuments && ( + + + + )} + {canViewEngineDocuments && ( + + + + )} {/* TODO: Remove layout once page template migration is over */} }> {canViewEngineAnalytics && ( @@ -101,16 +111,6 @@ export const EngineRouter: React.FC = () => { )} - {canViewEngineDocuments && ( - - - - )} - {canViewEngineDocuments && ( - - - - )} {canViewEngineSchema && (