Skip to content

Commit

Permalink
[App Search] New flyout to start a crawl with custom settings (#124999)
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron Hulcher authored Feb 15, 2022
1 parent 7b4c34e commit b11a829
Show file tree
Hide file tree
Showing 27 changed files with 2,038 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* 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 { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';

import React from 'react';

import { shallow, ShallowWrapper } from 'enzyme';

import { EuiButton, EuiButtonEmpty, EuiFlyout, EuiFlyoutFooter } from '@elastic/eui';

import { Loading } from '../../../../../shared/loading';
import { rerender } from '../../../../../test_helpers';

import { CrawlCustomSettingsFlyout } from './crawl_custom_settings_flyout';
import { CrawlCustomSettingsFlyoutCrawlDepthPanel } from './crawl_custom_settings_flyout_crawl_depth_panel';
import { CrawlCustomSettingsFlyoutDomainsPanel } from './crawl_custom_settings_flyout_domains_panel';
import { CrawlCustomSettingsFlyoutSeedUrlsPanel } from './crawl_custom_settings_flyout_seed_urls_panel';

const MOCK_VALUES = {
// CrawlCustomSettingsFlyoutLogic
isDataLoading: false,
isFormSubmitting: false,
isFlyoutVisible: true,
selectedDomainUrls: ['https://www.elastic.co'],
};

const MOCK_ACTIONS = {
// CrawlCustomSettingsFlyoutLogic
hideFlyout: jest.fn(),
onSelectDomainUrls: jest.fn(),
startCustomCrawl: jest.fn(),
};

describe('CrawlCustomSettingsFlyout', () => {
let wrapper: ShallowWrapper;

beforeEach(() => {
jest.clearAllMocks();
setMockValues(MOCK_VALUES);
setMockActions(MOCK_ACTIONS);

wrapper = shallow(<CrawlCustomSettingsFlyout />);
});

it('is empty when the flyout is hidden', () => {
setMockValues({
...MOCK_VALUES,
isFlyoutVisible: false,
});

rerender(wrapper);

expect(wrapper.isEmptyRender()).toBe(true);
});

it('renders as a modal when visible', () => {
expect(wrapper.is(EuiFlyout)).toBe(true);
});

it('can be closed', () => {
expect(wrapper.prop('onClose')).toEqual(MOCK_ACTIONS.hideFlyout);
expect(wrapper.find(EuiFlyoutFooter).find(EuiButtonEmpty).prop('onClick')).toEqual(
MOCK_ACTIONS.hideFlyout
);
});

it('lets the user customize their crawl', () => {
expect(wrapper.find(Loading)).toHaveLength(0);
for (const component of [
CrawlCustomSettingsFlyoutCrawlDepthPanel,
CrawlCustomSettingsFlyoutDomainsPanel,
CrawlCustomSettingsFlyoutSeedUrlsPanel,
]) {
expect(wrapper.find(component)).toHaveLength(1);
}
});

it('shows a loading state', () => {
setMockValues({
...MOCK_VALUES,
isDataLoading: true,
});

rerender(wrapper);

expect(wrapper.find(Loading)).toHaveLength(1);
for (const component of [
CrawlCustomSettingsFlyoutCrawlDepthPanel,
CrawlCustomSettingsFlyoutDomainsPanel,
CrawlCustomSettingsFlyoutSeedUrlsPanel,
]) {
expect(wrapper.find(component)).toHaveLength(0);
}
});

describe('submit button', () => {
it('is enabled by default', () => {
setMockValues({
...MOCK_VALUES,
selectedDomainUrls: [],
});

rerender(wrapper);

expect(wrapper.find(EuiFlyoutFooter).find(EuiButton).prop('disabled')).toEqual(true);
});

it('is disabled when no domains are selected', () => {
setMockValues({
...MOCK_VALUES,
selectedDomainUrls: [],
});

rerender(wrapper);

expect(wrapper.find(EuiFlyoutFooter).find(EuiButton).prop('disabled')).toEqual(true);
});

it('is disabled when data is loading', () => {
setMockValues({
...MOCK_VALUES,
isDataLoading: true,
});

rerender(wrapper);

expect(wrapper.find(EuiFlyoutFooter).find(EuiButton).prop('disabled')).toEqual(true);
});

it('shows a loading state when the user makes a request', () => {
setMockValues({
...MOCK_VALUES,
isFormSubmitting: true,
});

rerender(wrapper);

expect(wrapper.find(EuiFlyoutFooter).find(EuiButton).prop('isLoading')).toEqual(true);
});

it('starts a crawl and hides the modal', () => {
wrapper.find(EuiFlyoutFooter).find(EuiButton).simulate('click');

expect(MOCK_ACTIONS.startCustomCrawl).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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 from 'react';

import { useValues, useActions } from 'kea';

import {
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutFooter,
EuiFlyoutHeader,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';

import { i18n } from '@kbn/i18n';

import { CANCEL_BUTTON_LABEL } from '../../../../../shared/constants';
import { Loading } from '../../../../../shared/loading';

import { CrawlCustomSettingsFlyoutCrawlDepthPanel } from './crawl_custom_settings_flyout_crawl_depth_panel';
import { CrawlCustomSettingsFlyoutDomainsPanel } from './crawl_custom_settings_flyout_domains_panel';
import { CrawlCustomSettingsFlyoutLogic } from './crawl_custom_settings_flyout_logic';
import { CrawlCustomSettingsFlyoutSeedUrlsPanel } from './crawl_custom_settings_flyout_seed_urls_panel';

export const CrawlCustomSettingsFlyout: React.FC = () => {
const { isDataLoading, isFormSubmitting, isFlyoutVisible, selectedDomainUrls } = useValues(
CrawlCustomSettingsFlyoutLogic
);
const { hideFlyout, startCustomCrawl } = useActions(CrawlCustomSettingsFlyoutLogic);

if (!isFlyoutVisible) {
return null;
}

return (
<EuiFlyout ownFocus onClose={hideFlyout} size="m">
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2>
{i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlCustomSettingsFlyout.flyoutHeadTitle',
{
defaultMessage: 'Custom crawl configuration',
}
)}
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s">
<p>
{i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlCustomSettingsFlyout.flyoutHeaderDescription',
{
defaultMessage: 'Set up a one-time crawl with custom settings.',
}
)}
</p>
</EuiText>
</EuiFlyoutHeader>
<EuiFlyoutBody>
{isDataLoading ? (
<Loading />
) : (
<>
<CrawlCustomSettingsFlyoutCrawlDepthPanel />
<EuiSpacer />
<CrawlCustomSettingsFlyoutDomainsPanel />
<EuiSpacer />
<CrawlCustomSettingsFlyoutSeedUrlsPanel />
</>
)}
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={hideFlyout}>{CANCEL_BUTTON_LABEL}</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill
onClick={startCustomCrawl}
disabled={isDataLoading || selectedDomainUrls.length === 0}
isLoading={isFormSubmitting}
>
{i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlCustomSettingsFlyout.startCrawlButtonLabel',
{
defaultMessage: 'Apply and crawl now',
}
)}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</EuiFlyout>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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 { setMockActions, setMockValues } from '../../../../../__mocks__/kea_logic';

import React from 'react';

import { shallow } from 'enzyme';

import { EuiFieldNumber } from '@elastic/eui';

import { CrawlCustomSettingsFlyoutCrawlDepthPanel } from './crawl_custom_settings_flyout_crawl_depth_panel';

const MOCK_VALUES = {
// CrawlCustomSettingsFlyoutLogic
maxCrawlDepth: 5,
};

const MOCK_ACTIONS = {
// CrawlCustomSettingsFlyoutLogic
onSelectMaxCrawlDepth: jest.fn(),
};

describe('CrawlCustomSettingsFlyoutCrawlDepthPanel', () => {
beforeEach(() => {
jest.clearAllMocks();
setMockValues(MOCK_VALUES);
setMockActions(MOCK_ACTIONS);
});

it('allows the user to set max crawl depth', () => {
const wrapper = shallow(<CrawlCustomSettingsFlyoutCrawlDepthPanel />);
const crawlDepthField = wrapper.find(EuiFieldNumber);

expect(crawlDepthField.prop('value')).toEqual(5);

crawlDepthField.simulate('change', { target: { value: '10' } });

expect(MOCK_ACTIONS.onSelectMaxCrawlDepth).toHaveBeenCalledWith(10);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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, { ChangeEvent } from 'react';

import { useValues, useActions } from 'kea';

import {
EuiFieldNumber,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiPanel,
EuiText,
} from '@elastic/eui';

import { i18n } from '@kbn/i18n';

import { CrawlCustomSettingsFlyoutLogic } from './crawl_custom_settings_flyout_logic';

export const CrawlCustomSettingsFlyoutCrawlDepthPanel: React.FC = () => {
const { maxCrawlDepth } = useValues(CrawlCustomSettingsFlyoutLogic);
const { onSelectMaxCrawlDepth } = useActions(CrawlCustomSettingsFlyoutLogic);

return (
<EuiPanel hasBorder>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiFormRow
label={i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlCustomSettingsFlyout.maxCrawlDepthFieldLabel',
{
defaultMessage: 'Max crawl depth',
}
)}
>
<EuiFieldNumber
min={1}
value={maxCrawlDepth}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
onSelectMaxCrawlDepth(parseInt(e.target.value, 10))
}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="xs" color="subdued">
{i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlCustomSettingsFlyout.maxCrawlDepthFieldDescription',
{
defaultMessage:
'Set a max crawl depth to specify how many pages deep the crawler should traverse. Set the value to one (1) to limit the crawl to only the entry points.',
}
)}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
};
Loading

0 comments on commit b11a829

Please sign in to comment.