-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[App Search] New modal to crawl select domains (#124195)
- Loading branch information
Byron Hulcher
authored
Feb 8, 2022
1 parent
4394293
commit 97230e9
Showing
19 changed files
with
840 additions
and
22 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
.../components/crawler/components/crawl_select_domains_modal/crawl_select_domains_modal.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.crawlSelectDomainsModal { | ||
width: 50rem; | ||
max-width: 90%; | ||
} |
98 changes: 98 additions & 0 deletions
98
...ponents/crawler/components/crawl_select_domains_modal/crawl_select_domains_modal.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* 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 { EuiModal, EuiModalFooter, EuiButton, EuiButtonEmpty } from '@elastic/eui'; | ||
|
||
import { rerender } from '../../../../../test_helpers'; | ||
|
||
import { CrawlSelectDomainsModal } from './crawl_select_domains_modal'; | ||
import { SimplifiedSelectable } from './simplified_selectable'; | ||
|
||
const MOCK_VALUES = { | ||
// CrawlerLogic | ||
domains: [{ url: 'https://www.elastic.co' }, { url: 'https://www.swiftype.com' }], | ||
// CrawlSelectDomainsModalLogic | ||
selectedDomainUrls: ['https://www.elastic.co'], | ||
isModalVisible: true, | ||
}; | ||
|
||
const MOCK_ACTIONS = { | ||
// CrawlSelectDomainsModalLogic | ||
hideModal: jest.fn(), | ||
onSelectDomainUrls: jest.fn(), | ||
// CrawlerLogic | ||
startCrawl: jest.fn(), | ||
}; | ||
|
||
describe('CrawlSelectDomainsModal', () => { | ||
let wrapper: ShallowWrapper; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
setMockValues(MOCK_VALUES); | ||
setMockActions(MOCK_ACTIONS); | ||
|
||
wrapper = shallow(<CrawlSelectDomainsModal />); | ||
}); | ||
|
||
it('is empty when the modal is hidden', () => { | ||
setMockValues({ | ||
...MOCK_VALUES, | ||
isModalVisible: false, | ||
}); | ||
|
||
rerender(wrapper); | ||
|
||
expect(wrapper.isEmptyRender()).toBe(true); | ||
}); | ||
|
||
it('renders as a modal when visible', () => { | ||
expect(wrapper.is(EuiModal)).toBe(true); | ||
}); | ||
|
||
it('can be closed', () => { | ||
expect(wrapper.prop('onClose')).toEqual(MOCK_ACTIONS.hideModal); | ||
expect(wrapper.find(EuiModalFooter).find(EuiButtonEmpty).prop('onClick')).toEqual( | ||
MOCK_ACTIONS.hideModal | ||
); | ||
}); | ||
|
||
it('allows the user to select domains', () => { | ||
expect(wrapper.find(SimplifiedSelectable).props()).toEqual({ | ||
options: ['https://www.elastic.co', 'https://www.swiftype.com'], | ||
selectedOptions: ['https://www.elastic.co'], | ||
onChange: MOCK_ACTIONS.onSelectDomainUrls, | ||
}); | ||
}); | ||
|
||
describe('submit button', () => { | ||
it('is disabled when no domains are selected', () => { | ||
setMockValues({ | ||
...MOCK_VALUES, | ||
selectedDomainUrls: [], | ||
}); | ||
|
||
rerender(wrapper); | ||
|
||
expect(wrapper.find(EuiModalFooter).find(EuiButton).prop('disabled')).toEqual(true); | ||
}); | ||
|
||
it('starts a crawl and hides the modal', () => { | ||
wrapper.find(EuiModalFooter).find(EuiButton).simulate('click'); | ||
|
||
expect(MOCK_ACTIONS.startCrawl).toHaveBeenCalledWith({ | ||
domain_allowlist: MOCK_VALUES.selectedDomainUrls, | ||
}); | ||
}); | ||
}); | ||
}); |
109 changes: 109 additions & 0 deletions
109
...h/components/crawler/components/crawl_select_domains_modal/crawl_select_domains_modal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* | ||
* 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, | ||
EuiModal, | ||
EuiModalBody, | ||
EuiModalFooter, | ||
EuiModalHeader, | ||
EuiModalHeaderTitle, | ||
EuiNotificationBadge, | ||
} from '@elastic/eui'; | ||
|
||
import { i18n } from '@kbn/i18n'; | ||
|
||
import { CANCEL_BUTTON_LABEL } from '../../../../../shared/constants'; | ||
|
||
import { CrawlerLogic } from '../../crawler_logic'; | ||
|
||
import { CrawlSelectDomainsModalLogic } from './crawl_select_domains_modal_logic'; | ||
import { SimplifiedSelectable } from './simplified_selectable'; | ||
|
||
import './crawl_select_domains_modal.scss'; | ||
|
||
export const CrawlSelectDomainsModal: React.FC = () => { | ||
const { domains } = useValues(CrawlerLogic); | ||
const domainUrls = domains.map((domain) => domain.url); | ||
|
||
const crawlSelectDomainsModalLogic = CrawlSelectDomainsModalLogic({ domains }); | ||
const { isDataLoading, isModalVisible, selectedDomainUrls } = useValues( | ||
crawlSelectDomainsModalLogic | ||
); | ||
const { hideModal, onSelectDomainUrls } = useActions(crawlSelectDomainsModalLogic); | ||
|
||
const { startCrawl } = useActions(CrawlerLogic); | ||
|
||
if (!isModalVisible) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<EuiModal onClose={hideModal} className="crawlSelectDomainsModal"> | ||
<EuiModalHeader> | ||
<EuiFlexGroup alignItems="center" gutterSize="m"> | ||
<EuiFlexItem> | ||
<EuiModalHeaderTitle> | ||
{i18n.translate( | ||
'xpack.enterpriseSearch.appSearch.crawler.crawlSelectDomainsModal.modalHeaderTitle', | ||
{ | ||
defaultMessage: 'Crawl select domains', | ||
} | ||
)} | ||
</EuiModalHeaderTitle> | ||
</EuiFlexItem> | ||
<EuiNotificationBadge | ||
size="m" | ||
color={selectedDomainUrls.length > 0 ? 'accent' : 'subdued'} | ||
> | ||
{selectedDomainUrls.length} | ||
</EuiNotificationBadge> | ||
<EuiFlexItem grow={false}> | ||
{i18n.translate( | ||
'xpack.enterpriseSearch.appSearch.crawler.crawlSelectDomainsModal.selectedDescriptor', | ||
{ | ||
defaultMessage: 'selected', | ||
} | ||
)} | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiModalHeader> | ||
<EuiModalBody> | ||
<SimplifiedSelectable | ||
options={domainUrls} | ||
selectedOptions={selectedDomainUrls} | ||
onChange={onSelectDomainUrls} | ||
/> | ||
</EuiModalBody> | ||
<EuiModalFooter> | ||
<EuiButtonEmpty onClick={hideModal}>{CANCEL_BUTTON_LABEL}</EuiButtonEmpty> | ||
<EuiButton | ||
fill | ||
onClick={() => { | ||
startCrawl({ domain_allowlist: selectedDomainUrls }); | ||
}} | ||
disabled={selectedDomainUrls.length === 0} | ||
isLoading={isDataLoading} | ||
> | ||
{i18n.translate( | ||
'xpack.enterpriseSearch.appSearch.crawler.crawlSelectDomainsModal.startCrawlButtonLabel', | ||
{ | ||
defaultMessage: 'Apply and crawl now', | ||
} | ||
)} | ||
</EuiButton> | ||
</EuiModalFooter> | ||
</EuiModal> | ||
); | ||
}; |
100 changes: 100 additions & 0 deletions
100
...ts/crawler/components/crawl_select_domains_modal/crawl_select_domains_modal_logic.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* | ||
* 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 { LogicMounter } from '../../../../../__mocks__/kea_logic'; | ||
|
||
import { CrawlerLogic } from '../../crawler_logic'; | ||
|
||
import { CrawlSelectDomainsModalLogic } from './crawl_select_domains_modal_logic'; | ||
|
||
describe('CrawlSelectDomainsModalLogic', () => { | ||
const { mount } = new LogicMounter(CrawlSelectDomainsModalLogic); | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
mount(); | ||
}); | ||
|
||
it('has expected default values', () => { | ||
expect(CrawlSelectDomainsModalLogic.values).toEqual({ | ||
isDataLoading: false, | ||
isModalVisible: false, | ||
selectedDomainUrls: [], | ||
}); | ||
}); | ||
|
||
describe('actions', () => { | ||
describe('hideModal', () => { | ||
it('hides the modal', () => { | ||
CrawlSelectDomainsModalLogic.actions.hideModal(); | ||
|
||
expect(CrawlSelectDomainsModalLogic.values.isModalVisible).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('showModal', () => { | ||
it('shows the modal', () => { | ||
CrawlSelectDomainsModalLogic.actions.showModal(); | ||
|
||
expect(CrawlSelectDomainsModalLogic.values.isModalVisible).toBe(true); | ||
}); | ||
|
||
it('resets the selected options', () => { | ||
mount({ | ||
selectedDomainUrls: ['https://www.elastic.co', 'https://www.swiftype.com'], | ||
}); | ||
|
||
CrawlSelectDomainsModalLogic.actions.showModal(); | ||
|
||
expect(CrawlSelectDomainsModalLogic.values.selectedDomainUrls).toEqual([]); | ||
}); | ||
}); | ||
|
||
describe('onSelectDomainUrls', () => { | ||
it('saves the urls', () => { | ||
mount({ | ||
selectedDomainUrls: [], | ||
}); | ||
|
||
CrawlSelectDomainsModalLogic.actions.onSelectDomainUrls([ | ||
'https://www.elastic.co', | ||
'https://www.swiftype.com', | ||
]); | ||
|
||
expect(CrawlSelectDomainsModalLogic.values.selectedDomainUrls).toEqual([ | ||
'https://www.elastic.co', | ||
'https://www.swiftype.com', | ||
]); | ||
}); | ||
}); | ||
|
||
describe('[CrawlerLogic.actionTypes.startCrawl]', () => { | ||
it('enables loading state', () => { | ||
mount({ | ||
isDataLoading: false, | ||
}); | ||
|
||
CrawlerLogic.actions.startCrawl(); | ||
|
||
expect(CrawlSelectDomainsModalLogic.values.isDataLoading).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('[CrawlerLogic.actionTypes.onStartCrawlRequestComplete]', () => { | ||
it('disables loading state and hides the modal', () => { | ||
mount({ | ||
isDataLoading: true, | ||
isModalVisible: true, | ||
}); | ||
|
||
CrawlerLogic.actions.onStartCrawlRequestComplete(); | ||
|
||
expect(CrawlSelectDomainsModalLogic.values.isDataLoading).toBe(false); | ||
expect(CrawlSelectDomainsModalLogic.values.isModalVisible).toBe(false); | ||
}); | ||
}); | ||
}); | ||
}); |
67 changes: 67 additions & 0 deletions
67
...ponents/crawler/components/crawl_select_domains_modal/crawl_select_domains_modal_logic.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* 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 { kea, MakeLogicType } from 'kea'; | ||
|
||
import { CrawlerLogic } from '../../crawler_logic'; | ||
|
||
import { CrawlerDomain } from '../../types'; | ||
|
||
export interface CrawlSelectDomainsLogicProps { | ||
domains: CrawlerDomain[]; | ||
} | ||
|
||
export interface CrawlSelectDomainsLogicValues { | ||
isDataLoading: boolean; | ||
isModalVisible: boolean; | ||
selectedDomainUrls: string[]; | ||
} | ||
|
||
export interface CrawlSelectDomainsModalLogicActions { | ||
hideModal(): void; | ||
onSelectDomainUrls(domainUrls: string[]): { domainUrls: string[] }; | ||
showModal(): void; | ||
} | ||
|
||
export const CrawlSelectDomainsModalLogic = kea< | ||
MakeLogicType< | ||
CrawlSelectDomainsLogicValues, | ||
CrawlSelectDomainsModalLogicActions, | ||
CrawlSelectDomainsLogicProps | ||
> | ||
>({ | ||
path: ['enterprise_search', 'app_search', 'crawler', 'crawl_select_domains_modal'], | ||
actions: () => ({ | ||
hideModal: true, | ||
onSelectDomainUrls: (domainUrls) => ({ domainUrls }), | ||
showModal: true, | ||
}), | ||
reducers: () => ({ | ||
isDataLoading: [ | ||
false, | ||
{ | ||
[CrawlerLogic.actionTypes.startCrawl]: () => true, | ||
[CrawlerLogic.actionTypes.onStartCrawlRequestComplete]: () => false, | ||
}, | ||
], | ||
isModalVisible: [ | ||
false, | ||
{ | ||
showModal: () => true, | ||
hideModal: () => false, | ||
[CrawlerLogic.actionTypes.onStartCrawlRequestComplete]: () => false, | ||
}, | ||
], | ||
selectedDomainUrls: [ | ||
[], | ||
{ | ||
showModal: () => [], | ||
onSelectDomainUrls: (_, { domainUrls }) => domainUrls, | ||
}, | ||
], | ||
}), | ||
}); |
Oops, something went wrong.