parent
d335e42701
commit
3d23351f74
|
@ -109,8 +109,10 @@
|
|||
"@elastic/good": "^9.0.1-kibana3",
|
||||
"@elastic/node-crypto": "1.2.1",
|
||||
"@elastic/numeral": "^2.5.0",
|
||||
"@elastic/react-search-ui": "^1.5.0",
|
||||
"@elastic/request-crypto": "1.1.4",
|
||||
"@elastic/safer-lodash-set": "link:packages/elastic-safer-lodash-set",
|
||||
"@elastic/search-ui-app-search-connector": "^1.5.0",
|
||||
"@hapi/boom": "^7.4.11",
|
||||
"@hapi/cookie": "^10.1.2",
|
||||
"@hapi/good-squeeze": "5.2.1",
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import { DocumentCreationButton } from './document_creation_button';
|
||||
|
||||
describe('DocumentCreationButton', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render', () => {
|
||||
const wrapper = shallow(<DocumentCreationButton />);
|
||||
expect(wrapper.find(EuiButton).length).toEqual(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
export const DocumentCreationButton: React.FC = () => {
|
||||
return (
|
||||
<EuiButton fill={true} color="primary" data-test-subj="IndexDocumentsButton">
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.documents.indexDocuments', {
|
||||
defaultMessage: 'Index documents',
|
||||
})}
|
||||
</EuiButton>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 } from '../../../__mocks__/kea.mock';
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { DocumentCreationButton } from './document_creation_button';
|
||||
import { SearchExperience } from './search_experience';
|
||||
import { Documents } from '.';
|
||||
|
||||
describe('Documents', () => {
|
||||
const values = {
|
||||
isMetaEngine: false,
|
||||
myRole: { canManageEngineDocuments: true },
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues(values);
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<Documents engineBreadcrumb={['test']} />);
|
||||
expect(wrapper.find(SearchExperience).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders a DocumentCreationButton if the user can manage engine documents', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
myRole: { canManageEngineDocuments: true },
|
||||
});
|
||||
|
||||
const wrapper = shallow(<Documents engineBreadcrumb={['test']} />);
|
||||
expect(wrapper.find(DocumentCreationButton).exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('Meta Engines', () => {
|
||||
it('renders a Meta Engines message if this is a meta engine', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
isMetaEngine: true,
|
||||
});
|
||||
|
||||
const wrapper = shallow(<Documents engineBreadcrumb={['test']} />);
|
||||
expect(wrapper.find('[data-test-subj="MetaEnginesCallout"]').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render a Meta Engines message if this is not a meta engine', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
isMetaEngine: false,
|
||||
});
|
||||
|
||||
const wrapper = shallow(<Documents engineBreadcrumb={['test']} />);
|
||||
expect(wrapper.find('[data-test-subj="MetaEnginesCallout"]').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not render a DocumentCreationButton even if the user can manage engine documents', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
myRole: { canManageEngineDocuments: true },
|
||||
isMetaEngine: true,
|
||||
});
|
||||
|
||||
const wrapper = shallow(<Documents engineBreadcrumb={['test']} />);
|
||||
expect(wrapper.find(DocumentCreationButton).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,23 +6,26 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
EuiPageHeader,
|
||||
EuiPageHeaderSection,
|
||||
EuiTitle,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
} from '@elastic/eui';
|
||||
import { EuiPageHeader, EuiPageHeaderSection, EuiTitle, EuiCallOut, EuiSpacer } from '@elastic/eui';
|
||||
import { useValues } from 'kea';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DocumentCreationButton } from './document_creation_button';
|
||||
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
|
||||
import { FlashMessages } from '../../../shared/flash_messages';
|
||||
import { DOCUMENTS_TITLE } from './constants';
|
||||
import { EngineLogic } from '../engine';
|
||||
import { AppLogic } from '../../app_logic';
|
||||
import { SearchExperience } from './search_experience';
|
||||
|
||||
interface Props {
|
||||
engineBreadcrumb: string[];
|
||||
}
|
||||
|
||||
export const Documents: React.FC<Props> = ({ engineBreadcrumb }) => {
|
||||
const { isMetaEngine } = useValues(EngineLogic);
|
||||
const { myRole } = useValues(AppLogic);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SetPageChrome trail={[...engineBreadcrumb, DOCUMENTS_TITLE]} />
|
||||
|
@ -32,12 +35,36 @@ export const Documents: React.FC<Props> = ({ engineBreadcrumb }) => {
|
|||
<h1>{DOCUMENTS_TITLE}</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
{myRole.canManageEngineDocuments && !isMetaEngine && (
|
||||
<EuiPageHeaderSection>
|
||||
<DocumentCreationButton />
|
||||
</EuiPageHeaderSection>
|
||||
)}
|
||||
</EuiPageHeader>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentBody>
|
||||
<FlashMessages />
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
<FlashMessages />
|
||||
{isMetaEngine && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
data-test-subj="MetaEnginesCallout"
|
||||
iconType="iInCircle"
|
||||
title={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.metaEngineCallout.title',
|
||||
{
|
||||
defaultMessage: 'You are within a Meta Engine.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<p>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.documents.metaEngineCallout', {
|
||||
defaultMessage:
|
||||
'Meta Engines have many Source Engines. Visit your Source Engines to alter their documents.',
|
||||
})}
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
)}
|
||||
<SearchExperience />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
jest.mock('../hooks', () => ({
|
||||
useSearchContextActions: jest.fn(() => ({})),
|
||||
useSearchContextState: jest.fn(() => ({})),
|
||||
}));
|
||||
|
||||
import { useSearchContextState, useSearchContextActions } from '../hooks';
|
||||
|
||||
export const setMockSearchContextState = (values: object) => {
|
||||
(useSearchContextState as jest.Mock).mockImplementation(() => values);
|
||||
};
|
||||
export const setMockSearchContextActions = (actions: object) => {
|
||||
(useSearchContextActions as jest.Mock).mockImplementation(() => actions);
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const mockAction = jest.fn();
|
||||
|
||||
let mockSubcription: (state: object) => void;
|
||||
const mockDriver = {
|
||||
state: { foo: 'foo' },
|
||||
actions: { bar: mockAction },
|
||||
subscribeToStateChanges: jest.fn().mockImplementation((fn) => {
|
||||
mockSubcription = fn;
|
||||
}),
|
||||
unsubscribeToStateChanges: jest.fn(),
|
||||
};
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...(jest.requireActual('react') as object),
|
||||
useContext: jest.fn(() => ({
|
||||
driver: mockDriver,
|
||||
})),
|
||||
}));
|
||||
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
|
||||
import { useSearchContextState, useSearchContextActions } from './hooks';
|
||||
|
||||
describe('hooks', () => {
|
||||
describe('useSearchContextState', () => {
|
||||
const TestComponent = () => {
|
||||
const { foo } = useSearchContextState();
|
||||
return <div>{foo}</div>;
|
||||
};
|
||||
|
||||
let wrapper: ReactWrapper;
|
||||
beforeAll(() => {
|
||||
wrapper = mount(<TestComponent />);
|
||||
});
|
||||
|
||||
it('exposes search state', () => {
|
||||
expect(wrapper.text()).toEqual('foo');
|
||||
});
|
||||
|
||||
it('subscribes to state changes', () => {
|
||||
act(() => {
|
||||
mockSubcription({ foo: 'bar' });
|
||||
});
|
||||
|
||||
expect(wrapper.text()).toEqual('bar');
|
||||
});
|
||||
|
||||
it('unsubscribes to state changes when unmounted', () => {
|
||||
wrapper.unmount();
|
||||
|
||||
expect(mockDriver.unsubscribeToStateChanges).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('useSearchContextActions', () => {
|
||||
it('exposes actions', () => {
|
||||
const TestComponent = () => {
|
||||
const { bar } = useSearchContextActions();
|
||||
bar();
|
||||
return null;
|
||||
};
|
||||
|
||||
mount(<TestComponent />);
|
||||
expect(mockAction).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { useContext, useEffect, useState } from 'react';
|
||||
|
||||
// @ts-expect-error types are not available for this package yet
|
||||
import { SearchContext } from '@elastic/react-search-ui';
|
||||
|
||||
export const useSearchContextState = () => {
|
||||
const { driver } = useContext(SearchContext);
|
||||
const [state, setState] = useState(driver.state);
|
||||
|
||||
useEffect(() => {
|
||||
driver.subscribeToStateChanges((newState: object) => {
|
||||
setState(newState);
|
||||
});
|
||||
return () => {
|
||||
driver.unsubscribeToStateChanges();
|
||||
};
|
||||
}, [state]);
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export const useSearchContextActions = () => {
|
||||
const { driver } = useContext(SearchContext);
|
||||
return driver.actions;
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { SearchExperience } from './search_experience';
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
// @ts-expect-error types are not available for this package yet
|
||||
import { Paging, ResultsPerPage } from '@elastic/react-search-ui';
|
||||
|
||||
import { Pagination } from './pagination';
|
||||
|
||||
describe('Pagination', () => {
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<Pagination aria-label="foo" />);
|
||||
expect(wrapper.find(Paging).exists()).toBe(true);
|
||||
expect(wrapper.find(ResultsPerPage).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('passes aria-label through to Paging', () => {
|
||||
const wrapper = shallow(<Pagination aria-label="foo" />);
|
||||
expect(wrapper.find(Paging).prop('aria-label')).toEqual('foo');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
// @ts-expect-error types are not available for this package yet
|
||||
import { Paging, ResultsPerPage } from '@elastic/react-search-ui';
|
||||
import { PagingView, ResultsPerPageView } from './views';
|
||||
|
||||
export const Pagination: React.FC<{ 'aria-label': string }> = ({ 'aria-label': ariaLabel }) => (
|
||||
<EuiFlexGroup alignItems="center" className="documentsSearchExperience__pagingInfo">
|
||||
<EuiFlexItem>
|
||||
<Paging view={PagingView} aria-label={ariaLabel} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ResultsPerPage view={ResultsPerPageView} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
|
@ -0,0 +1,19 @@
|
|||
.documentsSearchExperience {
|
||||
.sui-results-container {
|
||||
flex-grow: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.documentsSearchExperience__sidebar {
|
||||
flex-grow: 1;
|
||||
min-width: $euiSize * 19;
|
||||
}
|
||||
|
||||
.documentsSearchExperience__content {
|
||||
flex-grow: 4;
|
||||
}
|
||||
|
||||
.documentsSearchExperience__pagingInfo {
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 '../../../../__mocks__/kea.mock';
|
||||
import { setMockValues } from '../../../../__mocks__';
|
||||
import '../../../../__mocks__/enterprise_search_url.mock';
|
||||
|
||||
import React from 'react';
|
||||
// @ts-expect-error types are not available for this package yet
|
||||
import { SearchProvider } from '@elastic/react-search-ui';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { SearchExperience } from './search_experience';
|
||||
|
||||
describe('SearchExperience', () => {
|
||||
const values = {
|
||||
engine: {
|
||||
name: 'some-engine',
|
||||
apiKey: '1234',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues(values);
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<SearchExperience />);
|
||||
expect(wrapper.find(SearchProvider).length).toBe(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { useValues } from 'kea';
|
||||
import { EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
// @ts-expect-error types are not available for this package yet;
|
||||
import { SearchProvider, SearchBox, Sorting } from '@elastic/react-search-ui';
|
||||
// @ts-expect-error types are not available for this package yet
|
||||
import AppSearchAPIConnector from '@elastic/search-ui-app-search-connector';
|
||||
|
||||
import './search_experience.scss';
|
||||
|
||||
import { EngineLogic } from '../../engine';
|
||||
import { externalUrl } from '../../../../shared/enterprise_search_url';
|
||||
|
||||
import { SearchBoxView, SortingView } from './views';
|
||||
import { SearchExperienceContent } from './search_experience_content';
|
||||
|
||||
const DEFAULT_SORT_OPTIONS = [
|
||||
{
|
||||
name: i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedDesc', {
|
||||
defaultMessage: 'Recently Uploaded (desc)',
|
||||
}),
|
||||
value: 'id',
|
||||
direction: 'desc',
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedAsc', {
|
||||
defaultMessage: 'Recently Uploaded (asc)',
|
||||
}),
|
||||
value: 'id',
|
||||
direction: 'asc',
|
||||
},
|
||||
];
|
||||
|
||||
export const SearchExperience: React.FC = () => {
|
||||
const { engine } = useValues(EngineLogic);
|
||||
const endpointBase = externalUrl.enterpriseSearchUrl;
|
||||
|
||||
// TODO const sortFieldsOptions = _flatten(fields.sortFields.map(fieldNameToSortOptions)) // we need to flatten this array since fieldNameToSortOptions returns an array of two sorting options
|
||||
const sortingOptions = [...DEFAULT_SORT_OPTIONS /* TODO ...sortFieldsOptions*/];
|
||||
|
||||
const connector = new AppSearchAPIConnector({
|
||||
cacheResponses: false,
|
||||
endpointBase,
|
||||
engineName: engine.name,
|
||||
searchKey: engine.apiKey,
|
||||
});
|
||||
|
||||
const searchProviderConfig = {
|
||||
alwaysSearchOnInitialLoad: true,
|
||||
apiConnector: connector,
|
||||
trackUrlState: false,
|
||||
initialState: {
|
||||
sortDirection: 'desc',
|
||||
sortField: 'id',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="documentsSearchExperience">
|
||||
<SearchProvider config={searchProviderConfig}>
|
||||
<SearchBox
|
||||
searchAsYouType={true}
|
||||
inputProps={{
|
||||
placeholder: i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.search.placeholder',
|
||||
{
|
||||
defaultMessage: 'Filter documents...',
|
||||
}
|
||||
),
|
||||
'aria-label': i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.search.ariaLabel',
|
||||
{
|
||||
defaultMessage: 'Filter documents',
|
||||
}
|
||||
),
|
||||
'data-test-subj': 'DocumentsFilterInput',
|
||||
}}
|
||||
view={SearchBoxView}
|
||||
/>
|
||||
<EuiSpacer size="xl" />
|
||||
<EuiFlexGroup direction="row">
|
||||
<EuiFlexItem className="documentsSearchExperience__sidebar">
|
||||
<Sorting
|
||||
className="documentsSearchExperience__sorting"
|
||||
sortOptions={sortingOptions}
|
||||
view={SortingView}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem className="documentsSearchExperience__content">
|
||||
<SearchExperienceContent />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</SearchProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 } from '../../../../__mocks__/kea.mock';
|
||||
import { setMockSearchContextState } from './__mocks__/hooks.mock';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { shallow, mount } from 'enzyme';
|
||||
// @ts-expect-error types are not available for this package yet
|
||||
import { Results } from '@elastic/react-search-ui';
|
||||
|
||||
import { ResultView } from './views';
|
||||
import { Pagination } from './pagination';
|
||||
import { SearchExperienceContent } from './search_experience_content';
|
||||
|
||||
describe('SearchExperienceContent', () => {
|
||||
const searchState = {
|
||||
resultSearchTerm: 'searchTerm',
|
||||
totalResults: 100,
|
||||
wasSearched: true,
|
||||
};
|
||||
const values = {
|
||||
engineName: 'engine1',
|
||||
isMetaEngine: false,
|
||||
myRole: { canManageEngineDocuments: true },
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
setMockValues(values);
|
||||
setMockSearchContextState(searchState);
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<SearchExperienceContent />);
|
||||
expect(wrapper.isEmptyRender()).toBe(false);
|
||||
});
|
||||
|
||||
it('passes engineName to the result view', () => {
|
||||
const props = {
|
||||
result: {
|
||||
foo: {
|
||||
raw: 'bar',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<SearchExperienceContent />);
|
||||
const resultView: any = wrapper.find(Results).prop('resultView');
|
||||
expect(resultView(props)).toEqual(<ResultView engineName="engine1" {...props} />);
|
||||
});
|
||||
|
||||
it('renders pagination', () => {
|
||||
const wrapper = shallow(<SearchExperienceContent />);
|
||||
expect(wrapper.find(Pagination).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders empty if a search was not performed yet', () => {
|
||||
setMockSearchContextState({
|
||||
...searchState,
|
||||
wasSearched: false,
|
||||
});
|
||||
const wrapper = shallow(<SearchExperienceContent />);
|
||||
expect(wrapper.isEmptyRender()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders results if a search was performed and there are more than 0 totalResults', () => {
|
||||
setMockSearchContextState({
|
||||
...searchState,
|
||||
wasSearched: true,
|
||||
totalResults: 10,
|
||||
});
|
||||
const wrapper = shallow(<SearchExperienceContent />);
|
||||
expect(wrapper.find('[data-test-subj="documentsSearchResults"]').length).toBe(1);
|
||||
});
|
||||
|
||||
it('renders a no results message if a non-empty search was performed and there are no results', () => {
|
||||
setMockSearchContextState({
|
||||
...searchState,
|
||||
resultSearchTerm: 'searchTerm',
|
||||
wasSearched: true,
|
||||
totalResults: 0,
|
||||
});
|
||||
const wrapper = shallow(<SearchExperienceContent />);
|
||||
expect(wrapper.find('[data-test-subj="documentsSearchResults"]').length).toBe(0);
|
||||
expect(wrapper.find('[data-test-subj="documentsSearchNoResults"]').length).toBe(1);
|
||||
});
|
||||
|
||||
describe('when an empty search was performed and there are no results, meaning there are no documents indexed', () => {
|
||||
beforeEach(() => {
|
||||
setMockSearchContextState({
|
||||
...searchState,
|
||||
resultSearchTerm: '',
|
||||
wasSearched: true,
|
||||
totalResults: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders a no documents message', () => {
|
||||
const wrapper = shallow(<SearchExperienceContent />);
|
||||
expect(wrapper.find('[data-test-subj="documentsSearchResults"]').length).toBe(0);
|
||||
expect(wrapper.find('[data-test-subj="documentsSearchNoDocuments"]').length).toBe(1);
|
||||
});
|
||||
|
||||
it('will include a button to index new documents', () => {
|
||||
const wrapper = mount(<SearchExperienceContent />);
|
||||
expect(
|
||||
wrapper
|
||||
.find(
|
||||
'[data-test-subj="documentsSearchNoDocuments"] [data-test-subj="IndexDocumentsButton"]'
|
||||
)
|
||||
.exists()
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('will include a button to documentation if this is a meta engine', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
isMetaEngine: true,
|
||||
});
|
||||
|
||||
const wrapper = mount(<SearchExperienceContent />);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(
|
||||
'[data-test-subj="documentsSearchNoDocuments"] [data-test-subj="IndexDocumentsButton"]'
|
||||
)
|
||||
.exists()
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(
|
||||
'[data-test-subj="documentsSearchNoDocuments"] [data-test-subj="documentsSearchDocsLink"]'
|
||||
)
|
||||
.exists()
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('will include a button to documentation if the user cannot manage documents', () => {
|
||||
setMockValues({
|
||||
...values,
|
||||
myRole: { canManageEngineDocuments: false },
|
||||
});
|
||||
|
||||
const wrapper = mount(<SearchExperienceContent />);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(
|
||||
'[data-test-subj="documentsSearchNoDocuments"] [data-test-subj="IndexDocumentsButton"]'
|
||||
)
|
||||
.exists()
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find(
|
||||
'[data-test-subj="documentsSearchNoDocuments"] [data-test-subj="documentsSearchDocsLink"]'
|
||||
)
|
||||
.exists()
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { EuiFlexGroup, EuiSpacer, EuiButton, EuiEmptyPrompt } from '@elastic/eui';
|
||||
// @ts-expect-error types are not available for this package yet
|
||||
import { Results, Paging, ResultsPerPage } from '@elastic/react-search-ui';
|
||||
import { useValues } from 'kea';
|
||||
|
||||
import { ResultView } from './views';
|
||||
import { Pagination } from './pagination';
|
||||
import { useSearchContextState } from './hooks';
|
||||
import { DocumentCreationButton } from '../document_creation_button';
|
||||
import { AppLogic } from '../../../app_logic';
|
||||
import { EngineLogic } from '../../engine';
|
||||
import { DOCS_PREFIX } from '../../../routes';
|
||||
|
||||
// TODO This is temporary until we create real Result type
|
||||
interface Result {
|
||||
[key: string]: {
|
||||
raw: string | string[] | number | number[] | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export const SearchExperienceContent: React.FC = () => {
|
||||
const { resultSearchTerm, totalResults, wasSearched } = useSearchContextState();
|
||||
|
||||
const { myRole } = useValues(AppLogic);
|
||||
const { engineName, isMetaEngine } = useValues(EngineLogic);
|
||||
|
||||
if (!wasSearched) return null;
|
||||
|
||||
if (totalResults) {
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="none" data-test-subj="documentsSearchResults">
|
||||
<Pagination
|
||||
aria-label={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.paging.ariaLabelTop',
|
||||
{
|
||||
defaultMessage: 'Search results paging at top of results',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<Results
|
||||
titleField="id"
|
||||
resultView={(props: { result: Result }) => {
|
||||
return <ResultView {...props} engineName={engineName} />;
|
||||
}}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<Pagination
|
||||
aria-label={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.paging.ariaLabelBottom',
|
||||
{
|
||||
defaultMessage: 'Search results paging at bottom of results',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
// If we have no results, but have a search term, show a message
|
||||
if (resultSearchTerm) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
data-test-subj="documentsSearchNoResults"
|
||||
body={i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.noResults', {
|
||||
defaultMessage: 'No results for "{resultSearchTerm}" yet!',
|
||||
values: {
|
||||
resultSearchTerm,
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// If we have no results AND no search term, show a CTA for the user to index documents
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
data-test-subj="documentsSearchNoDocuments"
|
||||
title={
|
||||
<h2>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.indexDocumentsTitle', {
|
||||
defaultMessage: 'No documents yet!',
|
||||
})}
|
||||
</h2>
|
||||
}
|
||||
body={i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.indexDocuments', {
|
||||
defaultMessage: 'Indexed documents will show up here.',
|
||||
})}
|
||||
actions={
|
||||
!isMetaEngine && myRole.canManageEngineDocuments ? (
|
||||
<DocumentCreationButton />
|
||||
) : (
|
||||
<EuiButton
|
||||
data-test-subj="documentsSearchDocsLink"
|
||||
href={`${DOCS_PREFIX}/indexing-documents-guide.html`}
|
||||
>
|
||||
{i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.indexingGuide', {
|
||||
defaultMessage: 'Read the indexing guide',
|
||||
})}
|
||||
</EuiButton>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { SearchBoxView } from './search_box_view';
|
||||
export { SortingView } from './sorting_view';
|
||||
export { ResultView } from './result_view';
|
||||
export { ResultsPerPageView } from './results_per_page_view';
|
||||
export { PagingView } from './paging_view';
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { EuiPagination } from '@elastic/eui';
|
||||
|
||||
import { PagingView } from './paging_view';
|
||||
|
||||
describe('PagingView', () => {
|
||||
const props = {
|
||||
current: 1,
|
||||
totalPages: 20,
|
||||
onChange: jest.fn(),
|
||||
'aria-label': 'paging view',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<PagingView {...props} />);
|
||||
expect(wrapper.find(EuiPagination).length).toBe(1);
|
||||
});
|
||||
|
||||
it('passes through totalPage', () => {
|
||||
const wrapper = shallow(<PagingView {...props} />);
|
||||
expect(wrapper.find(EuiPagination).prop('pageCount')).toEqual(20);
|
||||
});
|
||||
|
||||
it('passes through aria-label', () => {
|
||||
const wrapper = shallow(<PagingView {...props} />);
|
||||
expect(wrapper.find(EuiPagination).prop('aria-label')).toEqual('paging view');
|
||||
});
|
||||
|
||||
it('decrements current page by 1 and passes it through as activePage', () => {
|
||||
const wrapper = shallow(<PagingView {...props} />);
|
||||
expect(wrapper.find(EuiPagination).prop('activePage')).toEqual(0);
|
||||
});
|
||||
|
||||
it('calls onChange when onPageClick is triggered, and adds 1', () => {
|
||||
const wrapper = shallow(<PagingView {...props} />);
|
||||
const onPageClick: any = wrapper.find(EuiPagination).prop('onPageClick');
|
||||
onPageClick(3);
|
||||
expect(props.onChange).toHaveBeenCalledWith(4);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 { EuiPagination } from '@elastic/eui';
|
||||
|
||||
interface Props {
|
||||
current: number;
|
||||
totalPages: number;
|
||||
onChange(pageNumber: number): void;
|
||||
'aria-label': string;
|
||||
}
|
||||
|
||||
export const PagingView: React.FC<Props> = ({
|
||||
current,
|
||||
onChange,
|
||||
totalPages,
|
||||
'aria-label': ariaLabel,
|
||||
}) => (
|
||||
<EuiPagination
|
||||
pageCount={totalPages}
|
||||
activePage={current - 1} // EuiPagination is 0-indexed, Search UI is 1-indexed
|
||||
onPageClick={(page) => onChange(page + 1)} // EuiPagination is 0-indexed, Search UI is 1-indexed
|
||||
aria-label={ariaLabel}
|
||||
/>
|
||||
);
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
|
||||
import { ResultView } from '.';
|
||||
|
||||
describe('ResultView', () => {
|
||||
const result = {
|
||||
id: {
|
||||
raw: '1',
|
||||
},
|
||||
};
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<ResultView result={result} engineName="engine1" />);
|
||||
expect(wrapper.find('div').length).toBe(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 { EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { EuiLinkTo } from '../../../../../shared/react_router_helpers';
|
||||
|
||||
// TODO replace this with a real result type when we implement a more sophisticated
|
||||
// ResultView
|
||||
interface Result {
|
||||
[key: string]: {
|
||||
raw: string | string[] | number | number[] | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
interface Props {
|
||||
engineName: string;
|
||||
result: Result;
|
||||
}
|
||||
|
||||
export const ResultView: React.FC<Props> = ({ engineName, result }) => {
|
||||
// TODO Replace this entire component when we migrate StuiResult
|
||||
return (
|
||||
<li>
|
||||
<EuiPanel>
|
||||
<EuiLinkTo to={`/engines/${engineName}/documents/${result.id.raw}`}>
|
||||
<strong>{result.id.raw}</strong>
|
||||
</EuiLinkTo>
|
||||
{Object.entries(result).map(([key, value]) => (
|
||||
<div key={key} style={{ wordBreak: 'break-all' }}>
|
||||
{key}: {value.raw}
|
||||
</div>
|
||||
))}
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
</li>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { EuiSelect } from '@elastic/eui';
|
||||
|
||||
import { ResultsPerPageView } from '.';
|
||||
|
||||
describe('ResultsPerPageView', () => {
|
||||
const props = {
|
||||
options: [1, 2, 3],
|
||||
value: 1,
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<ResultsPerPageView {...props} />);
|
||||
expect(wrapper.find(EuiSelect).length).toBe(1);
|
||||
});
|
||||
|
||||
it('maps options to correct EuiSelect option', () => {
|
||||
const wrapper = shallow(<ResultsPerPageView {...props} />);
|
||||
expect(wrapper.find(EuiSelect).prop('options')).toEqual([
|
||||
{ text: 1, value: 1 },
|
||||
{ text: 2, value: 2 },
|
||||
{ text: 3, value: 3 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('passes through the value if it exists in options', () => {
|
||||
const wrapper = shallow(<ResultsPerPageView {...props} />);
|
||||
expect(wrapper.find(EuiSelect).prop('value')).toEqual(1);
|
||||
});
|
||||
|
||||
it('does not pass through the value if it does not exist in options', () => {
|
||||
const wrapper = shallow(
|
||||
<ResultsPerPageView
|
||||
{...{
|
||||
...props,
|
||||
value: 999,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find(EuiSelect).prop('value')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('passes through an onChange to EuiSelect', () => {
|
||||
const wrapper = shallow(<ResultsPerPageView {...props} />);
|
||||
const onChange: any = wrapper.find(EuiSelect).prop('onChange');
|
||||
onChange({ target: { value: 2 } });
|
||||
expect(props.onChange).toHaveBeenCalledWith(2);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { EuiSelect, EuiSelectOption } from '@elastic/eui';
|
||||
|
||||
const wrapResultsPerPageOptionForEuiSelect: (option: number) => EuiSelectOption = (option) => ({
|
||||
text: option,
|
||||
value: option,
|
||||
});
|
||||
|
||||
interface Props {
|
||||
options: number[];
|
||||
value: number;
|
||||
onChange(value: number): void;
|
||||
}
|
||||
|
||||
export const ResultsPerPageView: React.FC<Props> = ({ onChange, options, value }) => {
|
||||
// If we don't have the value in options, unset it
|
||||
const selectedValue = value && !options.includes(value) ? undefined : value;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EuiSelect
|
||||
options={options.map(wrapResultsPerPageOptionForEuiSelect)}
|
||||
value={selectedValue}
|
||||
prepend={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.search.resultsPerPage.show',
|
||||
{
|
||||
defaultMessage: 'Show:',
|
||||
}
|
||||
)}
|
||||
onChange={(event) => onChange(parseInt(event.target.value, 10))}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.search.resultsPerPage.ariaLabel',
|
||||
{
|
||||
defaultMessage: 'Number of results to show per page',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { EuiFieldSearch } from '@elastic/eui';
|
||||
|
||||
import { SearchBoxView } from './search_box_view';
|
||||
|
||||
describe('SearchBoxView', () => {
|
||||
const props = {
|
||||
onChange: jest.fn(),
|
||||
value: 'foo',
|
||||
inputProps: {
|
||||
placeholder: 'bar',
|
||||
'aria-label': 'foo',
|
||||
'data-test-subj': 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<SearchBoxView {...props} />);
|
||||
expect(wrapper.type()).toEqual(EuiFieldSearch);
|
||||
expect(wrapper.find(EuiFieldSearch).prop('value')).toEqual('foo');
|
||||
expect(wrapper.find(EuiFieldSearch).prop('placeholder')).toEqual('bar');
|
||||
});
|
||||
|
||||
it('passes through an onChange to EuiFieldSearch', () => {
|
||||
const wrapper = shallow(<SearchBoxView {...props} />);
|
||||
wrapper.prop('onChange')({ target: { value: 'test' } });
|
||||
expect(props.onChange).toHaveBeenCalledWith('test');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { EuiFieldSearch } from '@elastic/eui';
|
||||
|
||||
interface Props {
|
||||
inputProps: {
|
||||
placeholder: string;
|
||||
'aria-label': string;
|
||||
'data-test-subj': string;
|
||||
};
|
||||
value: string;
|
||||
onChange(value: string): void;
|
||||
}
|
||||
|
||||
export const SearchBoxView: React.FC<Props> = ({ onChange, value, inputProps }) => {
|
||||
return (
|
||||
<EuiFieldSearch
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
fullWidth={true}
|
||||
{...inputProps}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { EuiSelect } from '@elastic/eui';
|
||||
|
||||
import { SortingView } from '.';
|
||||
|
||||
describe('SortingView', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const props = {
|
||||
options: [{ label: 'Label', value: 'Value' }],
|
||||
value: 'Value',
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
|
||||
it('renders', () => {
|
||||
const wrapper = shallow(<SortingView {...props} />);
|
||||
expect(wrapper.find(EuiSelect).length).toBe(1);
|
||||
});
|
||||
|
||||
it('maps options to correct EuiSelect option', () => {
|
||||
const wrapper = shallow(<SortingView {...props} />);
|
||||
expect(wrapper.find(EuiSelect).prop('options')).toEqual([{ text: 'Label', value: 'Value' }]);
|
||||
});
|
||||
|
||||
it('passes through the value if it exists in options', () => {
|
||||
const wrapper = shallow(<SortingView {...props} />);
|
||||
expect(wrapper.find(EuiSelect).prop('value')).toEqual('Value');
|
||||
});
|
||||
|
||||
it('does not pass through the value if it does not exist in options', () => {
|
||||
const wrapper = shallow(
|
||||
<SortingView
|
||||
{...{
|
||||
...props,
|
||||
value: 'This value is not in Options',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find(EuiSelect).prop('value')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('passes through an onChange to EuiSelect', () => {
|
||||
const wrapper = shallow(<SortingView {...props} />);
|
||||
const onChange: any = wrapper.find(EuiSelect).prop('onChange');
|
||||
onChange({ target: { value: 'test' } });
|
||||
expect(props.onChange).toHaveBeenCalledWith('test');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { EuiSelect, EuiSelectOption } from '@elastic/eui';
|
||||
|
||||
interface Option {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const wrapSortingOptionForEuiSelect: (option: Option) => EuiSelectOption = (option) => ({
|
||||
text: option.label,
|
||||
value: option.value,
|
||||
});
|
||||
|
||||
const getValueFromOption: (option: Option) => string = (option) => option.value;
|
||||
|
||||
interface Props {
|
||||
options: Option[];
|
||||
value: string;
|
||||
onChange(value: string): void;
|
||||
}
|
||||
|
||||
export const SortingView: React.FC<Props> = ({ onChange, options, value }) => {
|
||||
// If we don't have the value in options, unset it
|
||||
const valuesFromOptions = options.map(getValueFromOption);
|
||||
const selectedValue = value && !valuesFromOptions.includes(value) ? undefined : value;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EuiSelect
|
||||
options={options.map(wrapSortingOptionForEuiSelect)}
|
||||
value={selectedValue}
|
||||
prepend={i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.sortBy', {
|
||||
defaultMessage: 'Sort by',
|
||||
})}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.enterpriseSearch.appSearch.documents.search.sortBy.ariaLabel',
|
||||
{
|
||||
defaultMessage: 'Sort results by',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -13,7 +13,7 @@ import { EngineDetails } from './types';
|
|||
|
||||
interface EngineValues {
|
||||
dataLoading: boolean;
|
||||
engine: EngineDetails | {};
|
||||
engine: Partial<EngineDetails>;
|
||||
engineName: string;
|
||||
isMetaEngine: boolean;
|
||||
isSampleEngine: boolean;
|
||||
|
|
249
yarn.lock
249
yarn.lock
|
@ -1419,6 +1419,13 @@
|
|||
dependencies:
|
||||
"@elastic/apm-rum-core" "^5.7.0"
|
||||
|
||||
"@elastic/app-search-javascript@^7.3.0":
|
||||
version "7.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/app-search-javascript/-/app-search-javascript-7.8.0.tgz#cbc7af6bcdd224518f7f595145d6ec744e0b165d"
|
||||
integrity sha512-EsAa/E/dQwBO72nrQ9YrXudP9KVY0sVUOvqPKZ3hBj9Mr3+MtWMyIKcyMf09bzdayk4qE+moetYDe5ahVbiA+Q==
|
||||
dependencies:
|
||||
object-hash "^1.3.0"
|
||||
|
||||
"@elastic/charts@24.3.0":
|
||||
version "24.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-24.3.0.tgz#5bb62143c2f941becbbbf91aafde849034b6330f"
|
||||
|
@ -1577,6 +1584,26 @@
|
|||
resolved "https://registry.yarnpkg.com/@elastic/numeral/-/numeral-2.5.0.tgz#8da714827fc278f17546601fdfe55f5c920e2bc5"
|
||||
integrity sha512-NVTuy9Wzblp6nOH86CXjWXTajHgJGn5Tk2l59/Z5cWFU14KlE+8/zqPTgZdxYABzBJFE3L7S07kJDMN8sDvTmA==
|
||||
|
||||
"@elastic/react-search-ui-views@1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/react-search-ui-views/-/react-search-ui-views-1.5.0.tgz#33988ae71588ad3e64f68c6e278d8262f5d59320"
|
||||
integrity sha512-Ur5Cya+B1em79ZNbPg+KYORuoHDM72LO5lqJeTNrW8WwRTEZi/vL21dOy47VYcSGVnCkttFD2BuyDOTMYFuExQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.4"
|
||||
autoprefixer "^9.6.1"
|
||||
downshift "^3.2.10"
|
||||
rc-pagination "^1.20.1"
|
||||
react-select "^2.4.4"
|
||||
|
||||
"@elastic/react-search-ui@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/react-search-ui/-/react-search-ui-1.5.0.tgz#d89304a2d6ad6377fe2a7f9202906f05e9bbc159"
|
||||
integrity sha512-fcfdD9v/87koM1dCsiAhJQz1Fb8Qz4NHEgRqdxZzSsqDaasTeSTRXX6UgbAiDidTa87mvGpT0SxAz8utAATpTQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.4"
|
||||
"@elastic/react-search-ui-views" "1.5.0"
|
||||
"@elastic/search-ui" "1.5.0"
|
||||
|
||||
"@elastic/request-crypto@1.1.4":
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/request-crypto/-/request-crypto-1.1.4.tgz#2189d5fea65f7afe1de9f5fa3d0dd420e93e3124"
|
||||
|
@ -1590,11 +1617,42 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@elastic/search-ui-app-search-connector@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/search-ui-app-search-connector/-/search-ui-app-search-connector-1.5.0.tgz#d379132c5015775acfaee5322ec019e9c0559ccc"
|
||||
integrity sha512-lHuXBjaMaN1fsm1taQMR/7gfpAg4XOsvZOi8u1AoufUw9kGr6Xc00Gznj1qTyH0Qebi2aSmY0NBN6pdIEGvvGQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.4"
|
||||
"@elastic/app-search-javascript" "^7.3.0"
|
||||
|
||||
"@elastic/search-ui@1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/search-ui/-/search-ui-1.5.0.tgz#32ea25f3a4fca10d0c56d535658415b276593f05"
|
||||
integrity sha512-UJzh3UcaAWKLjDIeJlVd0Okg+InLp8bijk+yOvCe4wtbVpTu5NCvAsfxo6mVTNnxS1ik9cRpMOqDT5sw6qyKoQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.4"
|
||||
date-fns "^1.30.1"
|
||||
deep-equal "^1.0.1"
|
||||
history "^4.9.0"
|
||||
qs "^6.7.0"
|
||||
|
||||
"@elastic/ui-ace@0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/ui-ace/-/ui-ace-0.2.3.tgz#5281aed47a79b7216c55542b0675e435692f20cd"
|
||||
integrity sha512-Nti5s2dplBPhSKRwJxG9JXTMOev4jVOWcnTJD1TOkJr1MUBYKVZcNcJtIVMSvahWGmP0B/UfO9q9lyRqdivkvQ==
|
||||
|
||||
"@emotion/babel-utils@^0.6.4":
|
||||
version "0.6.10"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/babel-utils/-/babel-utils-0.6.10.tgz#83dbf3dfa933fae9fc566e54fbb45f14674c6ccc"
|
||||
integrity sha512-/fnkM/LTEp3jKe++T0KyTszVGWNKPNOUJfjNKLO17BzQ6QPxgbg3whayom1Qr2oLFH3V92tDymU+dT5q676uow==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.6.6"
|
||||
"@emotion/memoize" "^0.6.6"
|
||||
"@emotion/serialize" "^0.9.1"
|
||||
convert-source-map "^1.5.1"
|
||||
find-root "^1.1.0"
|
||||
source-map "^0.7.2"
|
||||
|
||||
"@emotion/cache@^10.0.27", "@emotion/cache@^10.0.9":
|
||||
version "10.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
|
||||
|
@ -1631,6 +1689,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
||||
|
||||
"@emotion/hash@^0.6.2", "@emotion/hash@^0.6.6":
|
||||
version "0.6.6"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.6.6.tgz#62266c5f0eac6941fece302abad69f2ee7e25e44"
|
||||
integrity sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ==
|
||||
|
||||
"@emotion/is-prop-valid@0.8.8", "@emotion/is-prop-valid@^0.8.6", "@emotion/is-prop-valid@^0.8.8":
|
||||
version "0.8.8"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
|
||||
|
@ -1643,6 +1706,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
|
||||
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
||||
|
||||
"@emotion/memoize@^0.6.1", "@emotion/memoize@^0.6.6":
|
||||
version "0.6.6"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.6.6.tgz#004b98298d04c7ca3b4f50ca2035d4f60d2eed1b"
|
||||
integrity sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ==
|
||||
|
||||
"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16":
|
||||
version "0.11.16"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad"
|
||||
|
@ -1654,6 +1722,16 @@
|
|||
"@emotion/utils" "0.11.3"
|
||||
csstype "^2.5.7"
|
||||
|
||||
"@emotion/serialize@^0.9.1":
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.9.1.tgz#a494982a6920730dba6303eb018220a2b629c145"
|
||||
integrity sha512-zTuAFtyPvCctHBEL8KZ5lJuwBanGSutFEncqLn/m9T1a6a93smBStK+bZzcNPgj4QS8Rkw9VTwJGhRIUVO8zsQ==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.6.6"
|
||||
"@emotion/memoize" "^0.6.6"
|
||||
"@emotion/unitless" "^0.6.7"
|
||||
"@emotion/utils" "^0.8.2"
|
||||
|
||||
"@emotion/sheet@0.9.4":
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5"
|
||||
|
@ -1682,16 +1760,31 @@
|
|||
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
|
||||
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
||||
|
||||
"@emotion/stylis@^0.7.0":
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.7.1.tgz#50f63225e712d99e2b2b39c19c70fff023793ca5"
|
||||
integrity sha512-/SLmSIkN13M//53TtNxgxo57mcJk/UJIDFRKwOiLIBEyBHEcipgR6hNMQ/59Sl4VjCJ0Z/3zeAZyvnSLPG/1HQ==
|
||||
|
||||
"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.4":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||
|
||||
"@emotion/unitless@^0.6.2", "@emotion/unitless@^0.6.7":
|
||||
version "0.6.7"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.6.7.tgz#53e9f1892f725b194d5e6a1684a7b394df592397"
|
||||
integrity sha512-Arj1hncvEVqQ2p7Ega08uHLr1JuRYBuO5cIvcA+WWEQ5+VmkOE3ZXzl04NbQxeQpWX78G7u6MqxKuNX3wvYZxg==
|
||||
|
||||
"@emotion/utils@0.11.3":
|
||||
version "0.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924"
|
||||
integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
|
||||
|
||||
"@emotion/utils@^0.8.2":
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.8.2.tgz#576ff7fb1230185b619a75d258cbc98f0867a8dc"
|
||||
integrity sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw==
|
||||
|
||||
"@emotion/weak-memoize@0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
|
||||
|
@ -7593,6 +7686,19 @@ autobind-decorator@^1.3.4:
|
|||
resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-1.4.3.tgz#4c96ffa77b10622ede24f110f5dbbf56691417d1"
|
||||
integrity sha1-TJb/p3sQYi7eJPEQ9du/VmkUF9E=
|
||||
|
||||
autoprefixer@^9.6.1:
|
||||
version "9.8.6"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f"
|
||||
integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==
|
||||
dependencies:
|
||||
browserslist "^4.12.0"
|
||||
caniuse-lite "^1.0.30001109"
|
||||
colorette "^1.2.1"
|
||||
normalize-range "^0.1.2"
|
||||
num2fraction "^1.2.2"
|
||||
postcss "^7.0.32"
|
||||
postcss-value-parser "^4.1.0"
|
||||
|
||||
autoprefixer@^9.7.2, autoprefixer@^9.7.4:
|
||||
version "9.8.5"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.5.tgz#2c225de229ddafe1d1424c02791d0c3e10ccccaa"
|
||||
|
@ -7811,6 +7917,24 @@ babel-plugin-emotion@^10.0.20, babel-plugin-emotion@^10.0.27:
|
|||
find-root "^1.1.0"
|
||||
source-map "^0.5.7"
|
||||
|
||||
babel-plugin-emotion@^9.2.11:
|
||||
version "9.2.11"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-9.2.11.tgz#319c005a9ee1d15bb447f59fe504c35fd5807728"
|
||||
integrity sha512-dgCImifnOPPSeXod2znAmgc64NhaaOjGEHROR/M+lmStb3841yK1sgaDYAYMnlvWNz8GnpwIPN0VmNpbWYZ+VQ==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.0.0"
|
||||
"@emotion/babel-utils" "^0.6.4"
|
||||
"@emotion/hash" "^0.6.2"
|
||||
"@emotion/memoize" "^0.6.1"
|
||||
"@emotion/stylis" "^0.7.0"
|
||||
babel-plugin-macros "^2.0.0"
|
||||
babel-plugin-syntax-jsx "^6.18.0"
|
||||
convert-source-map "^1.5.0"
|
||||
find-root "^1.1.0"
|
||||
mkdirp "^0.5.1"
|
||||
source-map "^0.5.7"
|
||||
touch "^2.0.1"
|
||||
|
||||
babel-plugin-extract-import-names@1.6.16:
|
||||
version "1.6.16"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.16.tgz#b964004e794bdd62534c525db67d9e890d5cc079"
|
||||
|
@ -8084,7 +8208,7 @@ babel-preset-jest@^26.6.2:
|
|||
babel-plugin-transform-undefined-to-void "^6.9.4"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
|
||||
babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
|
||||
babel-runtime@6.x, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
|
||||
|
@ -9114,6 +9238,11 @@ caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.300010
|
|||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001150.tgz"
|
||||
integrity sha512-kiNKvihW0m36UhAFnl7bOAv0i1K1f6wpfVtTF5O5O82XzgtBnb05V0XeV3oZ968vfg2sRNChsHw8ASH2hDfoYQ==
|
||||
|
||||
caniuse-lite@^1.0.30001109:
|
||||
version "1.0.30001164"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001164.tgz#5bbfd64ca605d43132f13cc7fdabb17c3036bfdc"
|
||||
integrity sha512-G+A/tkf4bu0dSp9+duNiXc7bGds35DioCyC6vgK2m/rjA4Krpy5WeZgZyfH2f0wj2kI6yAWWucyap6oOwmY1mg==
|
||||
|
||||
caniuse-lite@^1.0.30001135:
|
||||
version "1.0.30001144"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001144.tgz#bca0fffde12f97e1127a351fec3bfc1971aa3b3d"
|
||||
|
@ -9883,7 +10012,7 @@ color@3.0.x:
|
|||
color-convert "^1.9.1"
|
||||
color-string "^1.5.2"
|
||||
|
||||
colorette@^1.2.0:
|
||||
colorette@^1.2.0, colorette@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
|
||||
integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
|
||||
|
@ -10044,6 +10173,11 @@ compression@^1.7.4:
|
|||
safe-buffer "5.1.2"
|
||||
vary "~1.1.2"
|
||||
|
||||
compute-scroll-into-view@^1.0.9:
|
||||
version "1.0.16"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.16.tgz#5b7bf4f7127ea2c19b750353d7ce6776a90ee088"
|
||||
integrity sha512-a85LHKY81oQnikatZYA90pufpZ6sQx++BoCxOEMsjpZx+ZnaKGQnCyCehTRr/1p9GBIAHTjcU9k71kSYWloLiQ==
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
|
@ -10403,6 +10537,19 @@ create-ecdh@^4.0.0:
|
|||
bn.js "^4.1.0"
|
||||
elliptic "^6.0.0"
|
||||
|
||||
create-emotion@^9.2.12:
|
||||
version "9.2.12"
|
||||
resolved "https://registry.yarnpkg.com/create-emotion/-/create-emotion-9.2.12.tgz#0fc8e7f92c4f8bb924b0fef6781f66b1d07cb26f"
|
||||
integrity sha512-P57uOF9NL2y98Xrbl2OuiDQUZ30GVmASsv5fbsjF4Hlraip2kyAvMm+2PoYUvFFw03Fhgtxk3RqZSm2/qHL9hA==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.6.2"
|
||||
"@emotion/memoize" "^0.6.1"
|
||||
"@emotion/stylis" "^0.7.0"
|
||||
"@emotion/unitless" "^0.6.2"
|
||||
csstype "^2.5.2"
|
||||
stylis "^3.5.0"
|
||||
stylis-rule-sheet "^0.0.10"
|
||||
|
||||
create-error-class@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
|
||||
|
@ -10726,6 +10873,11 @@ csstype@^2.2.0, csstype@^2.5.5, csstype@^2.5.7, csstype@^2.6.7:
|
|||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5"
|
||||
integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==
|
||||
|
||||
csstype@^2.5.2:
|
||||
version "2.6.14"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.14.tgz#004822a4050345b55ad4dcc00be1d9cf2f4296de"
|
||||
integrity sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==
|
||||
|
||||
cucumber-expressions@^5.0.13:
|
||||
version "5.0.18"
|
||||
resolved "https://registry.yarnpkg.com/cucumber-expressions/-/cucumber-expressions-5.0.18.tgz#6c70779efd3aebc5e9e7853938b1110322429596"
|
||||
|
@ -11202,6 +11354,11 @@ date-fns@^1.27.2:
|
|||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6"
|
||||
integrity sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==
|
||||
|
||||
date-fns@^1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
|
||||
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
|
||||
|
||||
date-now@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||
|
@ -11816,6 +11973,13 @@ dom-converter@~0.2:
|
|||
dependencies:
|
||||
utila "~0.4"
|
||||
|
||||
dom-helpers@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
|
||||
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
|
||||
dom-helpers@^5.0.0, dom-helpers@^5.0.1:
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b"
|
||||
|
@ -11956,6 +12120,16 @@ dotignore@^0.1.2:
|
|||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
|
||||
downshift@^3.2.10:
|
||||
version "3.4.8"
|
||||
resolved "https://registry.yarnpkg.com/downshift/-/downshift-3.4.8.tgz#06b7ad9e9c423a58e8a9049b2a00a5d19c7ef954"
|
||||
integrity sha512-dZL3iNL/LbpHNzUQAaVq/eTD1ocnGKKjbAl/848Q0KEp6t81LJbS37w3f93oD6gqqAnjdgM7Use36qZSipHXBw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.5"
|
||||
compute-scroll-into-view "^1.0.9"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.9.0"
|
||||
|
||||
dpdm@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/dpdm/-/dpdm-3.5.0.tgz#414402f21928694bc86cfe8e3583dc8fc97d013e"
|
||||
|
@ -12231,6 +12405,14 @@ emotion-theming@^10.0.19:
|
|||
"@emotion/weak-memoize" "0.2.5"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
emotion@^9.1.2:
|
||||
version "9.2.12"
|
||||
resolved "https://registry.yarnpkg.com/emotion/-/emotion-9.2.12.tgz#53925aaa005614e65c6e43db8243c843574d1ea9"
|
||||
integrity sha512-hcx7jppaI8VoXxIWEhxpDW7I+B4kq9RNzQLmsrF6LY8BGKqe2N+gFAQr0EfuFucFlPs2A9HM4+xNj4NeqEWIOQ==
|
||||
dependencies:
|
||||
babel-plugin-emotion "^9.2.11"
|
||||
create-emotion "^9.2.12"
|
||||
|
||||
enabled@1.0.x:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93"
|
||||
|
@ -20980,7 +21162,7 @@ object-filter-sequence@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/object-filter-sequence/-/object-filter-sequence-1.0.0.tgz#10bb05402fff100082b80d7e83991b10db411692"
|
||||
integrity sha512-CsubGNxhIEChNY4cXYuA6KXafztzHqzLLZ/y3Kasf3A+sa3lL9thq3z+7o0pZqzEinjXT6lXDPAfVWI59dUyzQ==
|
||||
|
||||
object-hash@^1.3.1:
|
||||
object-hash@^1.3.0, object-hash@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df"
|
||||
integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==
|
||||
|
@ -22680,6 +22862,11 @@ qs@6.7.0, qs@^6.4.0, qs@^6.5.1, qs@^6.6.0:
|
|||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@^6.7.0:
|
||||
version "6.9.4"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
|
||||
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
|
@ -22748,7 +22935,7 @@ raf-schd@^4.0.0, raf-schd@^4.0.2:
|
|||
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0"
|
||||
integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==
|
||||
|
||||
raf@^3.1.0, raf@^3.4.1:
|
||||
raf@^3.1.0, raf@^3.4.0, raf@^3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
|
||||
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
|
||||
|
@ -22854,6 +23041,16 @@ rbush@^3.0.1:
|
|||
dependencies:
|
||||
quickselect "^2.0.0"
|
||||
|
||||
rc-pagination@^1.20.1:
|
||||
version "1.21.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-1.21.1.tgz#24206cf4be96119baae8decd3f9ffac91cc2c4d3"
|
||||
integrity sha512-Z+iYLbrJOBKHdgoAjLhL9jOgb7nrbPzNmV31p0ikph010/Ov1+UkrauYzWhumUyR+GbRFi3mummdKW/WtlOewA==
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.6"
|
||||
prop-types "^15.5.7"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
rc@^1.0.1, rc@^1.2.8:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||
|
@ -23143,7 +23340,7 @@ react-hotkeys@2.0.0:
|
|||
dependencies:
|
||||
prop-types "^15.6.1"
|
||||
|
||||
react-input-autosize@^2.2.2:
|
||||
react-input-autosize@^2.2.1, react-input-autosize@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.2.tgz#fcaa7020568ec206bc04be36f4eb68e647c4d8c2"
|
||||
integrity sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==
|
||||
|
@ -23371,6 +23568,19 @@ react-router@5.2.0, react-router@^5.2.0:
|
|||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
|
||||
react-select@^2.4.4:
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-2.4.4.tgz#ba72468ef1060c7d46fbb862b0748f96491f1f73"
|
||||
integrity sha512-C4QPLgy9h42J/KkdrpVxNmkY6p4lb49fsrbDk/hRcZpX7JvZPNb6mGj+c5SzyEtBv1DmQ9oPH4NmhAFvCrg8Jw==
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
emotion "^9.1.2"
|
||||
memoize-one "^5.0.0"
|
||||
prop-types "^15.6.0"
|
||||
raf "^3.4.0"
|
||||
react-input-autosize "^2.2.1"
|
||||
react-transition-group "^2.2.1"
|
||||
|
||||
react-select@^3.0.8:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27"
|
||||
|
@ -23481,6 +23691,16 @@ react-tiny-virtual-list@^2.2.0:
|
|||
dependencies:
|
||||
prop-types "^15.5.7"
|
||||
|
||||
react-transition-group@^2.2.1:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
|
||||
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==
|
||||
dependencies:
|
||||
dom-helpers "^3.4.0"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
react-transition-group@^4.3.0:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
|
||||
|
@ -25555,7 +25775,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
|
|||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
source-map@^0.7.3:
|
||||
source-map@^0.7.2, source-map@^0.7.3:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
@ -26329,11 +26549,21 @@ styled-components@^5.1.0:
|
|||
shallowequal "^1.1.0"
|
||||
supports-color "^5.5.0"
|
||||
|
||||
stylis-rule-sheet@^0.0.10:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430"
|
||||
integrity sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==
|
||||
|
||||
stylis@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1"
|
||||
integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw==
|
||||
|
||||
stylis@^3.5.0:
|
||||
version "3.5.4"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
|
||||
integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==
|
||||
|
||||
subarg@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
|
||||
|
@ -27148,6 +27378,13 @@ topojson-client@^3.1.0:
|
|||
dependencies:
|
||||
commander "2"
|
||||
|
||||
touch@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/touch/-/touch-2.0.2.tgz#ca0b2a3ae3211246a61b16ba9e6cbf1596287164"
|
||||
integrity sha512-qjNtvsFXTRq7IuMLweVgFxmEuQ6gLbRs2jQxL80TtZ31dEKWYIxRXquij6w6VimyDek5hD3PytljHmEtAs2u0A==
|
||||
dependencies:
|
||||
nopt "~1.0.10"
|
||||
|
||||
touch@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
|
||||
|
|
Loading…
Reference in a new issue