Skip to content

Commit

Permalink
[App Search] Add accordion lists for Domains, Seed URLs, and Sitemap …
Browse files Browse the repository at this point in the history
…URLs to Crawl Details Flyout (#119682)

* New AccordionList component

* Add a domains accordion list to the CrawlDetailsPreview

* Add seedUrls to CrawlConfig

* Add a seed urls accordion list to the CrawlDetailsPreview

* Add sitemapUrls to CrawlConfig

* Add a sitemap urls accordion list to the CrawlDetailsPreview

* Disable arrow in AccordionList when items are empty

* Add spacers to CrawlDetailsPreview

* Type fix

* Sets a maxWidth on the flyout.

* Allows the raw JSON to be copied.

* Visual updates to the accordions.

* Linting

* Review and linting fixes.

* Styles the tables in the accordion list.

* Uses a subdued color for count badges when the count is zero.

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Davey Holler <daveyholler@hey.com>
  • Loading branch information
3 people authored Dec 1, 2021
1 parent 01af8ec commit 096fb43
Show file tree
Hide file tree
Showing 14 changed files with 330 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.appSearchAccordion--bordered {
border: $euiBorderThin;
border-radius: $euiBorderRadius;
padding: $euiSizeM;
}

.appSearchAccordion--bordered, .appSearchAccordion {
.euiBasicTable {
.euiTableRow > .euiTableRowCell {
border-bottom: none;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 '../../../../__mocks__/engine_logic.mock';

import React from 'react';

import { shallow, ShallowWrapper } from 'enzyme';

import { EuiAccordion, EuiIcon, EuiTitle, EuiInMemoryTable } from '@elastic/eui';

import { AccordionList } from './accordion_list';

const MOCK_PROPS = {
title: 'Test Items',
iconType: 'globe',
items: ['first item', 'second item'],
};

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

beforeAll(() => {
wrapper = shallow(<AccordionList {...MOCK_PROPS} />);
});

it('renders as an accordion with the passed in title and icon', () => {
expect(wrapper.is(EuiAccordion)).toBe(true);

const buttonContent = shallow(wrapper.prop('buttonContent'));

expect(buttonContent.find(EuiIcon).prop('type')).toEqual('globe');
expect(buttonContent.find(EuiTitle).children().text()).toEqual('Test Items');
});

it('shows the item count', () => {
const extraActionContent = shallow(wrapper.prop('extraAction'));

expect(extraActionContent.text()).toEqual('2');
});

it('contains an table displaying the items', () => {
const table = wrapper.find(EuiInMemoryTable);

expect(table.prop('items')).toEqual([{ item: 'first item' }, { item: 'second item' }]);

expect(table.prop('columns')[0].render({ item: 'first item' })).toEqual('first item');
});

it('is disabled when there are no items', () => {
const disabledWrapper = shallow(<AccordionList {...{ ...MOCK_PROPS, items: [] }} />);

expect(disabledWrapper.prop('arrowProps').isDisabled).toEqual(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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 {
EuiAccordion,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiInMemoryTable,
EuiNotificationBadge,
EuiSpacer,
EuiTitle,
IconType,
useGeneratedHtmlId,
} from '@elastic/eui';

import './accordion_list.scss';

interface Props {
hasBorder?: boolean;
iconType: IconType;
initialIsOpen?: boolean;
items: string[];
rowCount?: number;
title: string;
}

export const AccordionList: React.FC<Props> = ({
hasBorder,
iconType,
initialIsOpen,
items,
rowCount = 10,
title,
}) => {
const accordionId = useGeneratedHtmlId({
prefix: 'accordionList',
});

const showPagination = items.length > rowCount;

return (
<EuiAccordion
initialIsOpen={initialIsOpen}
arrowProps={{
isDisabled: items.length === 0,
}}
className={hasBorder ? 'appSearchAccordion--bordered' : 'appSearchAccordion'}
buttonContent={
<EuiFlexGroup direction="row" responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiIcon type={iconType} />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="s">
<h3>{title}</h3>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
}
id={accordionId}
extraAction={
<EuiNotificationBadge color={items.length > 0 ? 'accent' : 'subdued'} size="m">
{items.length}
</EuiNotificationBadge>
}
>
<EuiInMemoryTable
items={items.map((item) => ({ item }))}
columns={[
{
render: ({ item }: { item: string }) => item,
},
]}
pagination={
showPagination
? {
hidePerPageOptions: true,
}
: false
}
/>
{!showPagination && <EuiSpacer size="s" />}
</EuiAccordion>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ export const CrawlDetailsFlyout: React.FC = () => {
}

return (
<EuiFlyout ownFocus onClose={closeFlyout} aria-labelledby="CrawlDetailsFlyoutTitle">
<EuiFlyout
maxWidth="45rem"
ownFocus
onClose={closeFlyout}
aria-labelledby="CrawlDetailsFlyoutTitle"
>
<EuiFlyoutHeader hasBorder>
<EuiTitle size="m">
<h2 id="CrawlDetailsFlyoutTitle">
Expand Down Expand Up @@ -70,7 +75,7 @@ export const CrawlDetailsFlyout: React.FC = () => {
<>
{selectedTab === 'preview' && <CrawlDetailsPreview />}
{selectedTab === 'json' && (
<EuiCodeBlock language="json">
<EuiCodeBlock language="json" isCopyable>
{JSON.stringify(crawlRequestFromServer, null, 2)}
</EuiCodeBlock>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,77 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { setMockValues } from '../../../../../__mocks__/kea_logic';

import React from 'react';

import { shallow } from 'enzyme';
import { shallow, ShallowWrapper } from 'enzyme';

import { CrawlDetailValues } from '../../crawl_detail_logic';
import { CrawlerStatus, CrawlType } from '../../types';

import { AccordionList } from './accordion_list';
import { CrawlDetailsPreview } from './crawl_details_preview';

const MOCK_VALUES: Partial<CrawlDetailValues> = {
crawlRequest: {
id: '507f1f77bcf86cd799439011',
status: CrawlerStatus.Pending,
createdAt: 'Mon, 31 Aug 2020 17:00:00 +0000',
beganAt: null,
completedAt: null,
type: CrawlType.Full,
crawlConfig: {
domainAllowlist: ['https://www.elastic.co', 'https://www.swiftype.com'],
seedUrls: ['https://www.elastic.co/docs', 'https://www.swiftype.com/documentation'],
sitemapUrls: ['https://www.elastic.co/sitemap.xml', 'https://www.swiftype.com/sitemap.xml'],
},
},
};

describe('CrawlDetailsPreview', () => {
it('is empty', () => {
const wrapper = shallow(<CrawlDetailsPreview />);
it('is empty when a crawl request has not been loaded', () => {
setMockValues({
crawlRequest: null,
});

const wrapper = shallow(<CrawlDetailsPreview />);
expect(wrapper.isEmptyRender()).toBe(true);
});

describe('when a crawl request has been loaded', () => {
let wrapper: ShallowWrapper;

beforeAll(() => {
setMockValues(MOCK_VALUES);
wrapper = shallow(<CrawlDetailsPreview />);
});

it('contains a list of domains', () => {
const domainList = wrapper.find(AccordionList).at(0);

expect(domainList.prop('items')).toEqual([
'https://www.elastic.co',
'https://www.swiftype.com',
]);
});

it('contains a list of seed urls', () => {
const seedUrlList = wrapper.find(AccordionList).at(1);

expect(seedUrlList.prop('items')).toEqual([
'https://www.elastic.co/docs',
'https://www.swiftype.com/documentation',
]);
});

it('contains a list of sitemap urls', () => {
const sitemapUrlList = wrapper.find(AccordionList).at(2);

expect(sitemapUrlList.prop('items')).toEqual([
'https://www.elastic.co/sitemap.xml',
'https://www.swiftype.com/sitemap.xml',
]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,62 @@

import React from 'react';

export const CrawlDetailsPreview: React.FC = () => null;
import { useValues } from 'kea';

import { EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { CrawlDetailLogic } from '../../crawl_detail_logic';

import { AccordionList } from './accordion_list';

export const CrawlDetailsPreview: React.FC = () => {
const { crawlRequest } = useValues(CrawlDetailLogic);

if (crawlRequest === null) {
return null;
}

return (
<>
<AccordionList
hasBorder
initialIsOpen={crawlRequest.crawlConfig.domainAllowlist.length > 0}
title={i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlDetailsPreview.domainsTitle',
{
defaultMessage: 'Domains',
}
)}
iconType="globe"
items={crawlRequest.crawlConfig.domainAllowlist}
/>
<EuiSpacer size="s" />
<AccordionList
hasBorder
initialIsOpen={crawlRequest.crawlConfig.seedUrls.length > 0}
title={i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlDetailsPreview.seedUrlsTitle',
{
defaultMessage: 'Seed URLs',
}
)}
iconType="crosshairs"
items={crawlRequest.crawlConfig.seedUrls}
/>
<EuiSpacer size="s" />
<AccordionList
hasBorder
initialIsOpen={crawlRequest.crawlConfig.sitemapUrls.length > 0}
title={i18n.translate(
'xpack.enterpriseSearch.appSearch.crawler.crawlDetailsPreview.sitemapUrlsTitle',
{
defaultMessage: 'Sitemap URLs',
}
)}
iconType="visMapRegion"
items={crawlRequest.crawlConfig.sitemapUrls}
/>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const MOCK_EVENT: CrawlEvent = {
type: CrawlType.Full,
crawlConfig: {
domainAllowlist: ['https://www.elastic.co'],
seedUrls: [],
sitemapUrls: [],
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const values: { events: CrawlEvent[] } = {
type: CrawlType.Full,
crawlConfig: {
domainAllowlist: ['https://www.elastic.co'],
seedUrls: [],
sitemapUrls: [],
},
},
{
Expand All @@ -45,6 +47,8 @@ const values: { events: CrawlEvent[] } = {
type: CrawlType.Full,
crawlConfig: {
domainAllowlist: ['https://www.elastic.co'],
seedUrls: [],
sitemapUrls: [],
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const crawlRequestResponse: CrawlRequestWithDetailsFromServer = {
type: CrawlType.Full,
crawl_config: {
domain_allowlist: [],
seed_urls: [],
sitemap_urls: [],
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ describe('CrawlerLogic', () => {
type: CrawlType.Full,
crawlConfig: {
domainAllowlist: ['elastic.co'],
seedUrls: [],
sitemapUrls: [],
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ const events: CrawlEventFromServer[] = [
type: CrawlType.Full,
crawl_config: {
domain_allowlist: ['moviedatabase.com', 'swiftype.com'],
seed_urls: [],
sitemap_urls: [],
},
},
{
Expand All @@ -90,6 +92,8 @@ const events: CrawlEventFromServer[] = [
type: CrawlType.Partial,
crawl_config: {
domain_allowlist: ['swiftype.com'],
seed_urls: [],
sitemap_urls: [],
},
},
];
Expand Down
Loading

0 comments on commit 096fb43

Please sign in to comment.