Skip to content

Commit

Permalink
[App Search] Document Creation Buttons & bare-bones modal/logic (elas…
Browse files Browse the repository at this point in the history
…tic#85902) (elastic#86044)

* [Setup] Add EuiCardTo helper

* Set up very basic DocumentCreationLogic file modal management

* Create buttons and modal components

* Update EngineOverviewEmpty to use only DocumentCreationButtons

- Remove API section/example/constants for now: per design discussion, we'll be moving this section into the document creation modal
- Move preceding DOCUMENT_CREATION_DESCRIPTION paragraph directly into DocumentCreationButtons, since the Documents view will just use this verbatim

* Update Documents view Index Documents button to show DocumentsCreationModal

- with some extra logic to add a new creationStep view
+ some minor test cleanup / clarification for openDocumentCreation

* Update Crawler button with conditional check

- Forgot to port this over initially

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
Constance and kibanamachine authored Dec 16, 2020
1 parent 36fa4ad commit 3608c01
Show file tree
Hide file tree
Showing 16 changed files with 657 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';

import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCode, EuiLink } from '@elastic/eui';

import { DOCS_PREFIX } from '../../routes';

export const DOCUMENT_CREATION_DESCRIPTION = (
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.engine.documentCreation.description"
defaultMessage="There are three ways to send documents to your engine for indexing. You can paste raw JSON, upload a {jsonCode} file, or {postCode} to the {documentsApiLink} endpoint. Click on your choice below or see {apiStrong}."
values={{
jsonCode: <EuiCode>.json</EuiCode>,
postCode: <EuiCode>POST</EuiCode>,
documentsApiLink: (
<EuiLink target="_blank" href={`${DOCS_PREFIX}/indexing-documents-guide.html`}>
documents API
</EuiLink>
),
apiStrong: <strong>Indexing by API</strong>,
}}
/>
);

export const DOCUMENT_API_INDEXING_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.engine.documentCreation.api.title',
{ defaultMessage: 'Indexing by API' }
);

export const DOCUMENT_API_INDEXING_DESCRIPTION = (
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.engine.documentCreation.api.description"
defaultMessage="The {documentsApiLink} can be used to add new documents to your engine, update documents, retrieve documents by id, and delete documents. There are a variety of {clientLibrariesLink} to help you get started."
values={{
documentsApiLink: (
<EuiLink target="_blank" href={`${DOCS_PREFIX}/indexing-documents-guide.html`}>
documents API
</EuiLink>
),
clientLibrariesLink: (
<EuiLink target="_blank" href={`${DOCS_PREFIX}/api-clients.html`}>
client libraries
</EuiLink>
),
}}
/>
);
// TODO: This will be used shortly in an upcoming PR
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock';

import React from 'react';
import { shallow } from 'enzyme';
import { EuiCard } from '@elastic/eui';
import { EuiCardTo } from '../../../shared/react_router_helpers';

import { DocumentCreationButtons } from './';

describe('DocumentCreationButtons', () => {
const values = {
engineName: 'test-engine',
isSampleEngine: false,
myRole: { canViewEngineCrawler: true },
};
const actions = {
openDocumentCreation: jest.fn(),
};

beforeEach(() => {
jest.clearAllMocks();
setMockValues(values);
setMockActions(actions);
});

it('renders', () => {
const wrapper = shallow(<DocumentCreationButtons />);

expect(wrapper.find(EuiCard)).toHaveLength(3);
expect(wrapper.find(EuiCardTo)).toHaveLength(1);
});

it('renders with disabled buttons', () => {
const wrapper = shallow(<DocumentCreationButtons disabled />);

expect(wrapper.find(EuiCard).first().prop('isDisabled')).toEqual(true);
expect(wrapper.find(EuiCardTo).prop('isDisabled')).toEqual(true);
});

it('opens the DocumentCreationModal on click', () => {
const wrapper = shallow(<DocumentCreationButtons />);

wrapper.find(EuiCard).at(0).simulate('click');
expect(actions.openDocumentCreation).toHaveBeenCalledWith('text');

wrapper.find(EuiCard).at(1).simulate('click');
expect(actions.openDocumentCreation).toHaveBeenCalledWith('file');

wrapper.find(EuiCard).at(2).simulate('click');
expect(actions.openDocumentCreation).toHaveBeenCalledWith('api');
});

describe('crawler card', () => {
it('renders the crawler button with a link to the crawler page', () => {
const wrapper = shallow(<DocumentCreationButtons />);

expect(wrapper.find(EuiCardTo).prop('to')).toEqual('/engines/test-engine/crawler');
});

it('does not render the crawler button if the user does not have access', () => {
setMockValues({ ...values, myRole: { canViewEngineCrawler: false } });
const wrapper = shallow(<DocumentCreationButtons />);

expect(wrapper.find(EuiCardTo)).toHaveLength(0);
});

it('does not render the crawler button for the sample engine', () => {
setMockValues({ ...values, isSampleEngine: true });
const wrapper = shallow(<DocumentCreationButtons />);

expect(wrapper.find(EuiCardTo)).toHaveLength(0);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { useActions, useValues } from 'kea';

import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiText,
EuiCode,
EuiLink,
EuiSpacer,
EuiFlexGrid,
EuiFlexItem,
EuiCard,
EuiIcon,
} from '@elastic/eui';

import { EuiCardTo } from '../../../shared/react_router_helpers';
import { DOCS_PREFIX, getEngineRoute, ENGINE_CRAWLER_PATH } from '../../routes';
import { AppLogic } from '../../app_logic';
import { EngineLogic } from '../engine';

import { DocumentCreationLogic } from './';

interface Props {
disabled?: boolean;
}

export const DocumentCreationButtons: React.FC<Props> = ({ disabled = false }) => {
const { openDocumentCreation } = useActions(DocumentCreationLogic);

const { engineName, isSampleEngine } = useValues(EngineLogic);
const {
myRole: { canViewEngineCrawler },
} = useValues(AppLogic);
const showCrawlerLink = canViewEngineCrawler && !isSampleEngine;
const crawlerLink = getEngineRoute(engineName) + ENGINE_CRAWLER_PATH;

return (
<>
<EuiText color="subdued">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.appSearch.documentCreation.description"
defaultMessage="There are four ways to send documents to your engine for indexing. You can paste raw JSON, upload a {jsonCode} file, {postCode} to the {documentsApiLink} endpoint, or test drive the new Elastic Crawler (beta) to automatically index documents from a URL. Click on your choice below."
values={{
jsonCode: <EuiCode>.json</EuiCode>,
postCode: <EuiCode>POST</EuiCode>,
documentsApiLink: (
<EuiLink target="_blank" href={`${DOCS_PREFIX}/indexing-documents-guide.html`}>
documents API
</EuiLink>
),
}}
/>
</p>
</EuiText>
<EuiSpacer />
<EuiFlexGrid columns={showCrawlerLink ? 2 : 3}>
<EuiFlexItem>
<EuiCard
title={i18n.translate(
'xpack.enterpriseSearch.appSearch.documentCreation.buttons.text',
{ defaultMessage: 'Paste JSON' }
)}
description=""
icon={<EuiIcon type="indexEdit" size="xxl" color="primary" />}
data-test-subj="IndexingPasteJSONButton"
onClick={() => openDocumentCreation('text')}
isDisabled={disabled}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
title={i18n.translate(
'xpack.enterpriseSearch.appSearch.documentCreation.buttons.file',
{ defaultMessage: 'Upload a JSON file' }
)}
description=""
icon={<EuiIcon type="exportAction" size="xxl" color="primary" />}
onClick={() => openDocumentCreation('file')}
isDisabled={disabled}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
title={i18n.translate('xpack.enterpriseSearch.appSearch.documentCreation.buttons.api', {
defaultMessage: 'Index from API',
})}
description=""
icon={<EuiIcon type="editorCodeBlock" size="xxl" color="primary" />}
onClick={() => openDocumentCreation('api')}
isDisabled={disabled}
/>
</EuiFlexItem>
{showCrawlerLink && (
<EuiFlexItem>
<EuiCardTo
title={i18n.translate(
'xpack.enterpriseSearch.appSearch.documentCreation.buttons.crawl',
{ defaultMessage: 'Use the Crawler' }
)}
description=""
icon={<EuiIcon type="globe" size="xxl" color="primary" />}
betaBadgeLabel={i18n.translate(
'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTitle',
{ defaultMessage: 'Beta' }
)}
betaBadgeTooltipContent={i18n.translate(
'xpack.enterpriseSearch.appSearch.documentCreation.buttons.betaTooltip',
{
defaultMessage:
'The Elastic Crawler is not GA. Please help us by reporting any bugs.',
}
)}
to={crawlerLink}
isDisabled={disabled}
/>
</EuiFlexItem>
)}
</EuiFlexGrid>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { resetContext } from 'kea';

import { DocumentCreationStep } from './types';
import { DocumentCreationLogic } from './';

describe('DocumentCreationLogic', () => {
const DEFAULT_VALUES = {
isDocumentCreationOpen: false,
creationMode: 'text',
creationStep: DocumentCreationStep.AddDocuments,
};

const mount = () => {
resetContext({});
DocumentCreationLogic.mount();
};

beforeEach(() => {
jest.clearAllMocks();
});

it('has expected default values', () => {
mount();
expect(DocumentCreationLogic.values).toEqual(DEFAULT_VALUES);
});

describe('actions', () => {
describe('showCreationModes', () => {
beforeAll(() => {
mount();
DocumentCreationLogic.actions.showCreationModes();
});

const EXPECTED_VALUES = {
...DEFAULT_VALUES,
isDocumentCreationOpen: true,
creationStep: DocumentCreationStep.ShowCreationModes,
};

describe('isDocumentCreationOpen', () => {
it('should be set to true', () => {
expect(DocumentCreationLogic.values).toEqual({
...EXPECTED_VALUES,
isDocumentCreationOpen: true,
});
});
});

describe('creationStep', () => {
it('should be set to ShowCreationModes', () => {
expect(DocumentCreationLogic.values).toEqual({
...EXPECTED_VALUES,
creationStep: DocumentCreationStep.ShowCreationModes,
});
});
});
});

describe('openDocumentCreation', () => {
beforeAll(() => {
mount();
DocumentCreationLogic.actions.openDocumentCreation('api');
});

const EXPECTED_VALUES = {
...DEFAULT_VALUES,
isDocumentCreationOpen: true,
creationStep: DocumentCreationStep.AddDocuments,
creationMode: 'api',
};

describe('isDocumentCreationOpen', () => {
it('should be set to true', () => {
expect(DocumentCreationLogic.values).toEqual({
...EXPECTED_VALUES,
isDocumentCreationOpen: true,
});
});
});

describe('creationStep', () => {
it('should be set to AddDocuments', () => {
expect(DocumentCreationLogic.values).toEqual({
...EXPECTED_VALUES,
creationStep: DocumentCreationStep.AddDocuments,
});
});
});

describe('creationMode', () => {
it('should be set to the provided value', () => {
expect(DocumentCreationLogic.values).toEqual({
...EXPECTED_VALUES,
creationMode: 'api',
});
});
});
});

describe('closeDocumentCreation', () => {
describe('isDocumentCreationOpen', () => {
it('should be set to false', () => {
mount();
DocumentCreationLogic.actions.closeDocumentCreation();

expect(DocumentCreationLogic.values).toEqual({
...DEFAULT_VALUES,
isDocumentCreationOpen: false,
});
});
});
});

describe('setCreationStep', () => {
describe('creationStep', () => {
it('should be set to the provided value', () => {
mount();
DocumentCreationLogic.actions.setCreationStep(DocumentCreationStep.ShowSuccessSummary);

expect(DocumentCreationLogic.values).toEqual({
...DEFAULT_VALUES,
creationStep: 3,
});
});
});
});
});
});
Loading

0 comments on commit 3608c01

Please sign in to comment.