diff --git a/package.json b/package.json index 814c1c3585ee65..97f888b19b3d8f 100644 --- a/package.json +++ b/package.json @@ -112,8 +112,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", diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.test.tsx new file mode 100644 index 00000000000000..a62c735f1b6bfa --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.test.tsx @@ -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(); + expect(wrapper.find(EuiButton).length).toEqual(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.tsx new file mode 100644 index 00000000000000..cd6815ac4ba93e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_creation_button.tsx @@ -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 ( + + {i18n.translate('xpack.enterpriseSearch.appSearch.documents.indexDocuments', { + defaultMessage: 'Index documents', + })} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.test.tsx new file mode 100644 index 00000000000000..8940cc259c6476 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.test.tsx @@ -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(); + 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(); + 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(); + 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(); + 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(); + expect(wrapper.find(DocumentCreationButton).exists()).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx index 023ae06767abe2..f7881dc991ae6d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/documents.tsx @@ -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 = ({ engineBreadcrumb }) => { + const { isMetaEngine } = useValues(EngineLogic); + const { myRole } = useValues(AppLogic); + return ( <> @@ -32,12 +35,36 @@ export const Documents: React.FC = ({ engineBreadcrumb }) => {

{DOCUMENTS_TITLE}

+ {myRole.canManageEngineDocuments && !isMetaEngine && ( + + + + )} - - - - - + + {isMetaEngine && ( + <> + +

+ {i18n.translate('xpack.enterpriseSearch.appSearch.documents.metaEngineCallout', { + defaultMessage: + 'Meta Engines have many Source Engines. Visit your Source Engines to alter their documents.', + })} +

+
+ + + )} + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/__mocks__/hooks.mock.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/__mocks__/hooks.mock.ts new file mode 100644 index 00000000000000..c29f1204b369f3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/__mocks__/hooks.mock.ts @@ -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); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/hooks.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/hooks.test.tsx new file mode 100644 index 00000000000000..e9d374ed89a6f6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/hooks.test.tsx @@ -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
{foo}
; + }; + + let wrapper: ReactWrapper; + beforeAll(() => { + wrapper = mount(); + }); + + 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(); + expect(mockAction).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/hooks.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/hooks.ts new file mode 100644 index 00000000000000..25a38421ead685 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/hooks.ts @@ -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; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/index.ts new file mode 100644 index 00000000000000..9b09b3180e3e18 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/index.ts @@ -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'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/pagination.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/pagination.test.tsx new file mode 100644 index 00000000000000..b63e332d415b82 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/pagination.test.tsx @@ -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(); + expect(wrapper.find(Paging).exists()).toBe(true); + expect(wrapper.find(ResultsPerPage).exists()).toBe(true); + }); + + it('passes aria-label through to Paging', () => { + const wrapper = shallow(); + expect(wrapper.find(Paging).prop('aria-label')).toEqual('foo'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/pagination.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/pagination.tsx new file mode 100644 index 00000000000000..7f4e6660e088fa --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/pagination.tsx @@ -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 }) => ( + + + + + + + + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss new file mode 100644 index 00000000000000..cbc72dbffe57a3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss @@ -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; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx new file mode 100644 index 00000000000000..750d00311255ce --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx @@ -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(); + expect(wrapper.find(SearchProvider).length).toBe(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx new file mode 100644 index 00000000000000..49cc573b686bc2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx @@ -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 ( +
+ + + + + + + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx new file mode 100644 index 00000000000000..22a63f653a2941 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx @@ -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(); + expect(wrapper.isEmptyRender()).toBe(false); + }); + + it('passes engineName to the result view', () => { + const props = { + result: { + foo: { + raw: 'bar', + }, + }, + }; + + const wrapper = shallow(); + const resultView: any = wrapper.find(Results).prop('resultView'); + expect(resultView(props)).toEqual(); + }); + + it('renders pagination', () => { + const wrapper = shallow(); + expect(wrapper.find(Pagination).exists()).toBe(true); + }); + + it('renders empty if a search was not performed yet', () => { + setMockSearchContextState({ + ...searchState, + wasSearched: false, + }); + const wrapper = shallow(); + 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(); + 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(); + 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(); + 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(); + 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(); + + 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(); + + 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); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx new file mode 100644 index 00000000000000..938c8930f4dd14 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx @@ -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 ( + + + + { + return ; + }} + /> + + + + ); + } + + // If we have no results, but have a search term, show a message + if (resultSearchTerm) { + return ( + + ); + } + + // If we have no results AND no search term, show a CTA for the user to index documents + return ( + + {i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.indexDocumentsTitle', { + defaultMessage: 'No documents yet!', + })} + + } + body={i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.indexDocuments', { + defaultMessage: 'Indexed documents will show up here.', + })} + actions={ + !isMetaEngine && myRole.canManageEngineDocuments ? ( + + ) : ( + + {i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.indexingGuide', { + defaultMessage: 'Read the indexing guide', + })} + + ) + } + /> + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/index.ts new file mode 100644 index 00000000000000..8c88fc81d3a3c2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/index.ts @@ -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'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/paging_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/paging_view.test.tsx new file mode 100644 index 00000000000000..32468c153949f6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/paging_view.test.tsx @@ -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(); + expect(wrapper.find(EuiPagination).length).toBe(1); + }); + + it('passes through totalPage', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiPagination).prop('pageCount')).toEqual(20); + }); + + it('passes through aria-label', () => { + const wrapper = shallow(); + 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(); + expect(wrapper.find(EuiPagination).prop('activePage')).toEqual(0); + }); + + it('calls onChange when onPageClick is triggered, and adds 1', () => { + const wrapper = shallow(); + const onPageClick: any = wrapper.find(EuiPagination).prop('onPageClick'); + onPageClick(3); + expect(props.onChange).toHaveBeenCalledWith(4); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/paging_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/paging_view.tsx new file mode 100644 index 00000000000000..aafaac38269c1f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/paging_view.tsx @@ -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 = ({ + current, + onChange, + totalPages, + 'aria-label': ariaLabel, +}) => ( + onChange(page + 1)} // EuiPagination is 0-indexed, Search UI is 1-indexed + aria-label={ariaLabel} + /> +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx new file mode 100644 index 00000000000000..73ddf16e01074e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx @@ -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(); + expect(wrapper.find('div').length).toBe(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx new file mode 100644 index 00000000000000..bf472ec3bb21ec --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx @@ -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 = ({ engineName, result }) => { + // TODO Replace this entire component when we migrate StuiResult + return ( +
  • + + + {result.id.raw} + + {Object.entries(result).map(([key, value]) => ( +
    + {key}: {value.raw} +
    + ))} +
    + +
  • + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/results_per_page_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/results_per_page_view.test.tsx new file mode 100644 index 00000000000000..eea91e475de94b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/results_per_page_view.test.tsx @@ -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(); + expect(wrapper.find(EuiSelect).length).toBe(1); + }); + + it('maps options to correct EuiSelect option', () => { + const wrapper = shallow(); + 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(); + 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( + + ); + expect(wrapper.find(EuiSelect).prop('value')).toBeUndefined(); + }); + + it('passes through an onChange to EuiSelect', () => { + const wrapper = shallow(); + const onChange: any = wrapper.find(EuiSelect).prop('onChange'); + onChange({ target: { value: 2 } }); + expect(props.onChange).toHaveBeenCalledWith(2); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/results_per_page_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/results_per_page_view.tsx new file mode 100644 index 00000000000000..5152f1191fcb2f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/results_per_page_view.tsx @@ -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 = ({ onChange, options, value }) => { + // If we don't have the value in options, unset it + const selectedValue = value && !options.includes(value) ? undefined : value; + + return ( +
    + 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', + } + )} + /> +
    + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/search_box_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/search_box_view.test.tsx new file mode 100644 index 00000000000000..59033621e356e5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/search_box_view.test.tsx @@ -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(); + 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(); + wrapper.prop('onChange')({ target: { value: 'test' } }); + expect(props.onChange).toHaveBeenCalledWith('test'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/search_box_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/search_box_view.tsx new file mode 100644 index 00000000000000..002c9f84810ff7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/search_box_view.tsx @@ -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 = ({ onChange, value, inputProps }) => { + return ( + onChange(event.target.value)} + fullWidth={true} + {...inputProps} + /> + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/sorting_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/sorting_view.test.tsx new file mode 100644 index 00000000000000..40d6695d4eb6f6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/sorting_view.test.tsx @@ -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(); + expect(wrapper.find(EuiSelect).length).toBe(1); + }); + + it('maps options to correct EuiSelect option', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiSelect).prop('options')).toEqual([{ text: 'Label', value: 'Value' }]); + }); + + it('passes through the value if it exists in options', () => { + const wrapper = shallow(); + 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( + + ); + expect(wrapper.find(EuiSelect).prop('value')).toBeUndefined(); + }); + + it('passes through an onChange to EuiSelect', () => { + const wrapper = shallow(); + const onChange: any = wrapper.find(EuiSelect).prop('onChange'); + onChange({ target: { value: 'test' } }); + expect(props.onChange).toHaveBeenCalledWith('test'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/sorting_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/sorting_view.tsx new file mode 100644 index 00000000000000..db56dfcca286ec --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/sorting_view.tsx @@ -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 = ({ 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 ( +
    + onChange(event.target.value)} + aria-label={i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.sortBy.ariaLabel', + { + defaultMessage: 'Sort results by', + } + )} + /> +
    + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts index 51896becd87034..e1ce7cea0fa914 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts @@ -13,7 +13,7 @@ import { EngineDetails } from './types'; interface EngineValues { dataLoading: boolean; - engine: EngineDetails | {}; + engine: Partial; engineName: string; isMetaEngine: boolean; isSampleEngine: boolean; diff --git a/yarn.lock b/yarn.lock index 39ca2a4aa1ce78..66d69fce9ef264 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1374,6 +1374,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.2.0": version "24.2.0" resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-24.2.0.tgz#c670315b74184c463e6ad4015cf89c196bd5eb58" @@ -1532,6 +1539,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" @@ -1545,11 +1572,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" @@ -1586,6 +1644,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" @@ -1598,6 +1661,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" @@ -1609,6 +1677,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" @@ -1637,16 +1715,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" @@ -7519,6 +7612,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" @@ -7737,6 +7843,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" @@ -8010,7 +8134,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= @@ -9040,6 +9164,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" @@ -9795,7 +9924,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== @@ -9951,6 +10080,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" @@ -10305,6 +10439,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" @@ -10628,6 +10775,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" @@ -11104,6 +11256,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" @@ -11804,6 +11961,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" @@ -11944,6 +12108,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" @@ -12219,6 +12393,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" @@ -21072,7 +21254,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== @@ -22817,6 +22999,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" @@ -22885,7 +23072,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== @@ -22991,6 +23178,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.7, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -23280,7 +23477,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== @@ -23508,6 +23705,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" @@ -23618,6 +23828,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" @@ -25707,7 +25927,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== @@ -26482,11 +26702,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== + stylus-lookup@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" @@ -27314,6 +27544,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"