Skip to content

Commit

Permalink
[App Search] Convert Documents views to new page template + minor UI …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
Constance committed Jun 21, 2021
1 parent f2ca7fc commit 3673019
Show file tree
Hide file tree
Showing 11 changed files with 90 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => (
<EuiPanel color="subdued" grow={false}>
<EuiEmptyPrompt
data-test-subj="EmptyDocumentPrompt"
iconType="documents"
title={
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.documents.empty.title', {
defaultMessage: 'Add your first documents',
})}
</h2>
}
body={
<p>
{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.',
})}
</p>
}
actions={
<EuiButton
size="s"
target="_blank"
iconType="popout"
href={`${DOCS_PREFIX}/indexing-documents-guide.html`}
>
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.documents.empty.buttonLabel', {
defaultMessage: 'Read the documents guide',
})}
</EuiButton>
}
/>
</EuiPanel>
<EuiEmptyPrompt
data-test-subj="EmptyDocumentPrompt"
iconType="documents"
title={
<h2>
{i18n.translate('xpack.enterpriseSearch.appSearch.documents.empty.title', {
defaultMessage: 'Add your first documents',
})}
</h2>
}
body={
<p>
{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.',
})}
</p>
}
actions={
<EuiButton
size="s"
target="_blank"
iconType="popout"
href={`${DOCS_PREFIX}/indexing-documents-guide.html`}
>
{i18n.translate('xpack.enterpriseSearch.appSearch.engine.documents.empty.buttonLabel', {
defaultMessage: 'Read the documents guide',
})}
</EuiButton>
}
/>
);
Original file line number Diff line number Diff line change
Expand Up @@ -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 '.';
Expand Down Expand Up @@ -45,7 +46,7 @@ describe('DocumentDetail', () => {

it('renders', () => {
const wrapper = shallow(<DocumentDetail />);
expect(wrapper.find(EuiPageContent).length).toBe(1);
expect(wrapper.find(EuiPanel).length).toBe(1);
});

it('initializes data on mount', () => {
Expand All @@ -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(<DocumentDetail />);

expect(wrapper.find(Loading).length).toBe(1);
});

describe('field values list', () => {
let columns: any;

Expand Down Expand Up @@ -102,8 +92,7 @@ describe('DocumentDetail', () => {

it('will delete the document when the delete button is pressed', () => {
const wrapper = shallow(<DocumentDetail />);
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');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -52,10 +43,6 @@ export const DocumentDetail: React.FC = () => {
};
}, []);

if (dataLoading) {
return <Loading />;
}

const columns: Array<EuiBasicTableColumn<FieldDetails>> = [
{
name: i18n.translate('xpack.enterpriseSearch.appSearch.documentDetail.fieldHeader', {
Expand All @@ -74,11 +61,11 @@ export const DocumentDetail: React.FC = () => {
];

return (
<>
<SetPageChrome trail={getEngineBreadcrumbs([DOCUMENTS_TITLE, documentTitle])} />
<EuiPageHeader
pageTitle={DOCUMENT_DETAIL_TITLE(documentTitle)}
rightSideItems={[
<AppSearchPageTemplate
pageChrome={getEngineBreadcrumbs([DOCUMENTS_TITLE, documentTitle])}
pageHeader={{
pageTitle: DOCUMENT_DETAIL_TITLE(documentTitle),
rightSideItems: [
<EuiButton
color="danger"
iconType="trash"
Expand All @@ -87,14 +74,13 @@ export const DocumentDetail: React.FC = () => {
>
{DELETE_BUTTON_LABEL}
</EuiButton>,
]}
/>
<EuiPageContent hasBorder>
<EuiPageContentBody>
<FlashMessages />
<EuiBasicTable columns={columns} items={fields} />
</EuiPageContentBody>
</EuiPageContent>
</>
],
}}
isLoading={dataLoading}
>
<EuiPanel hasBorder>
<EuiBasicTable columns={columns} items={fields} />
</EuiPanel>
</AppSearchPageTemplate>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -22,6 +22,7 @@ import { Documents } from '.';
describe('Documents', () => {
const values = {
isMetaEngine: false,
engine: { document_count: 1 },
myRole: { canManageEngineDocuments: true },
};

Expand All @@ -36,17 +37,14 @@ 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,
myRole: { canManageEngineDocuments: true },
});

const wrapper = shallow(<Documents />);
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', () => {
Expand All @@ -56,7 +54,7 @@ describe('Documents', () => {
});

const wrapper = shallow(<Documents />);
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', () => {
Expand All @@ -67,7 +65,7 @@ describe('Documents', () => {
});

const wrapper = shallow(<Documents />);
expect(getHeader(wrapper).find(DocumentCreationButton).exists()).toBe(false);
expect(getPageHeaderActions(wrapper).find(DocumentCreationButton).exists()).toBe(false);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<SetPageChrome trail={getEngineBreadcrumbs([DOCUMENTS_TITLE])} />
<EuiPageHeader
pageTitle={DOCUMENTS_TITLE}
rightSideItems={
myRole.canManageEngineDocuments && !isMetaEngine
? [<DocumentCreationButton />]
: undefined
}
/>
<FlashMessages />
<AppSearchPageTemplate
pageChrome={getEngineBreadcrumbs([DOCUMENTS_TITLE])}
pageHeader={{
pageTitle: DOCUMENTS_TITLE,
rightSideItems:
myRole.canManageEngineDocuments && !isMetaEngine ? [<DocumentCreationButton />] : [],
}}
isEmptyState={!engine.document_count}
emptyState={<EmptyState />}
>
{isMetaEngine && (
<>
<EuiCallOut
Expand All @@ -61,6 +58,6 @@ export const Documents: React.FC = () => {
</>
)}
<SearchExperience />
</>
</AppSearchPageTemplate>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

.documentsSearchExperience__content {
flex-grow: 4;
position: relative;
}

.documentsSearchExperience__pagingInfo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(<SearchExperience />);

expect(wrapper.find(EmptyState)).toHaveLength(1);
expect(wrapper.find(SearchExperienceContent)).toHaveLength(0);
});

describe('when there are no selected filter fields', () => {
let wrapper: ShallowWrapper;
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -141,11 +140,7 @@ export const SearchExperience: React.FC = () => {
)}
</EuiFlexItem>
<EuiFlexItem className="documentsSearchExperience__content">
{engine.document_count && engine.document_count > 0 ? (
<SearchExperienceContent />
) : (
<EmptyState />
)}
<SearchExperienceContent />
</EuiFlexItem>
</EuiFlexGroup>
</SearchProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(<SearchExperienceContent />);
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', () => {
Expand Down
Loading

0 comments on commit 3673019

Please sign in to comment.