[App Search] Convert Curations pages to new page template (#102835)
* Update CurationRouter - Remove breadcrumbs set in router (will get set by page template) - Set up a curation breadcrumb helper for DRYness - Remove NotFound route - curation ID 404 handling will be used instead * Convert Curations page to new page template + move Empty State from table to top level * Convert Curation creation page to new page template * Convert single Curation page to new page template + remove breadcrumb prop * Update router * [Polish] Copy changes from Davey - see https://github.com/elastic/kibana/pull/101958/files - Per https://elastic.github.io/eui/#/guidelines/writing we shouldn't be using "new", so I removed that also * [UI polish] Add plus icon to create button - To match other create buttons across app
This commit is contained in:
parent
cf12c031cf
commit
e582549500
13 changed files with 110 additions and 114 deletions
|
@ -18,7 +18,7 @@ export const CURATIONS_OVERVIEW_TITLE = i18n.translate(
|
||||||
);
|
);
|
||||||
export const CREATE_NEW_CURATION_TITLE = i18n.translate(
|
export const CREATE_NEW_CURATION_TITLE = i18n.translate(
|
||||||
'xpack.enterpriseSearch.appSearch.engine.curations.create.title',
|
'xpack.enterpriseSearch.appSearch.engine.curations.create.title',
|
||||||
{ defaultMessage: 'Create new curation' }
|
{ defaultMessage: 'Create a curation' }
|
||||||
);
|
);
|
||||||
export const MANAGE_CURATION_TITLE = i18n.translate(
|
export const MANAGE_CURATION_TITLE = i18n.translate(
|
||||||
'xpack.enterpriseSearch.appSearch.engine.curations.manage.title',
|
'xpack.enterpriseSearch.appSearch.engine.curations.manage.title',
|
||||||
|
|
|
@ -8,16 +8,13 @@
|
||||||
import '../../../../__mocks__/shallow_useeffect.mock';
|
import '../../../../__mocks__/shallow_useeffect.mock';
|
||||||
import { setMockActions, setMockValues } from '../../../../__mocks__/kea_logic';
|
import { setMockActions, setMockValues } from '../../../../__mocks__/kea_logic';
|
||||||
import { mockUseParams } from '../../../../__mocks__/react_router';
|
import { mockUseParams } from '../../../../__mocks__/react_router';
|
||||||
|
import '../../../__mocks__/engine_logic.mock';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
|
|
||||||
import { EuiPageHeader } from '@elastic/eui';
|
import { rerender, getPageTitle, getPageHeaderActions } from '../../../../test_helpers';
|
||||||
|
|
||||||
import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome';
|
|
||||||
import { Loading } from '../../../../shared/loading';
|
|
||||||
import { rerender } from '../../../../test_helpers';
|
|
||||||
|
|
||||||
jest.mock('./curation_logic', () => ({ CurationLogic: jest.fn() }));
|
jest.mock('./curation_logic', () => ({ CurationLogic: jest.fn() }));
|
||||||
import { CurationLogic } from './curation_logic';
|
import { CurationLogic } from './curation_logic';
|
||||||
|
@ -27,9 +24,6 @@ import { AddResultFlyout } from './results';
|
||||||
import { Curation } from './';
|
import { Curation } from './';
|
||||||
|
|
||||||
describe('Curation', () => {
|
describe('Curation', () => {
|
||||||
const props = {
|
|
||||||
curationsBreadcrumb: ['Engines', 'some-engine', 'Curations'],
|
|
||||||
};
|
|
||||||
const values = {
|
const values = {
|
||||||
dataLoading: false,
|
dataLoading: false,
|
||||||
queries: ['query A', 'query B'],
|
queries: ['query A', 'query B'],
|
||||||
|
@ -47,39 +41,34 @@ describe('Curation', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders', () => {
|
it('renders', () => {
|
||||||
const wrapper = shallow(<Curation {...props} />);
|
const wrapper = shallow(<Curation />);
|
||||||
|
|
||||||
expect(wrapper.find(EuiPageHeader).prop('pageTitle')).toEqual('Manage curation');
|
expect(getPageTitle(wrapper)).toEqual('Manage curation');
|
||||||
expect(wrapper.find(SetPageChrome).prop('trail')).toEqual([
|
expect(wrapper.prop('pageChrome')).toEqual([
|
||||||
...props.curationsBreadcrumb,
|
'Engines',
|
||||||
|
'some-engine',
|
||||||
|
'Curations',
|
||||||
'query A, query B',
|
'query A, query B',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a loading component on page load', () => {
|
|
||||||
setMockValues({ ...values, dataLoading: true });
|
|
||||||
const wrapper = shallow(<Curation {...props} />);
|
|
||||||
|
|
||||||
expect(wrapper.find(Loading)).toHaveLength(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders the add result flyout when open', () => {
|
it('renders the add result flyout when open', () => {
|
||||||
setMockValues({ ...values, isFlyoutOpen: true });
|
setMockValues({ ...values, isFlyoutOpen: true });
|
||||||
const wrapper = shallow(<Curation {...props} />);
|
const wrapper = shallow(<Curation />);
|
||||||
|
|
||||||
expect(wrapper.find(AddResultFlyout)).toHaveLength(1);
|
expect(wrapper.find(AddResultFlyout)).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('initializes CurationLogic with a curationId prop from URL param', () => {
|
it('initializes CurationLogic with a curationId prop from URL param', () => {
|
||||||
mockUseParams.mockReturnValueOnce({ curationId: 'hello-world' });
|
mockUseParams.mockReturnValueOnce({ curationId: 'hello-world' });
|
||||||
shallow(<Curation {...props} />);
|
shallow(<Curation />);
|
||||||
|
|
||||||
expect(CurationLogic).toHaveBeenCalledWith({ curationId: 'hello-world' });
|
expect(CurationLogic).toHaveBeenCalledWith({ curationId: 'hello-world' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls loadCuration on page load & whenever the curationId URL param changes', () => {
|
it('calls loadCuration on page load & whenever the curationId URL param changes', () => {
|
||||||
mockUseParams.mockReturnValueOnce({ curationId: 'cur-123456789' });
|
mockUseParams.mockReturnValueOnce({ curationId: 'cur-123456789' });
|
||||||
const wrapper = shallow(<Curation {...props} />);
|
const wrapper = shallow(<Curation />);
|
||||||
expect(actions.loadCuration).toHaveBeenCalledTimes(1);
|
expect(actions.loadCuration).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
mockUseParams.mockReturnValueOnce({ curationId: 'cur-987654321' });
|
mockUseParams.mockReturnValueOnce({ curationId: 'cur-987654321' });
|
||||||
|
@ -92,9 +81,8 @@ describe('Curation', () => {
|
||||||
let confirmSpy: jest.SpyInstance;
|
let confirmSpy: jest.SpyInstance;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
const wrapper = shallow(<Curation {...props} />);
|
const wrapper = shallow(<Curation />);
|
||||||
const headerActions = wrapper.find(EuiPageHeader).prop('rightSideItems');
|
restoreDefaultsButton = getPageHeaderActions(wrapper).childAt(0);
|
||||||
restoreDefaultsButton = shallow(headerActions![0] as React.ReactElement);
|
|
||||||
|
|
||||||
confirmSpy = jest.spyOn(window, 'confirm');
|
confirmSpy = jest.spyOn(window, 'confirm');
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,26 +10,19 @@ import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { useValues, useActions } from 'kea';
|
import { useValues, useActions } from 'kea';
|
||||||
|
|
||||||
import { EuiPageHeader, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
|
import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
|
||||||
|
|
||||||
import { FlashMessages } from '../../../../shared/flash_messages';
|
|
||||||
import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome';
|
|
||||||
import { BreadcrumbTrail } from '../../../../shared/kibana_chrome/generate_breadcrumbs';
|
|
||||||
import { Loading } from '../../../../shared/loading';
|
|
||||||
|
|
||||||
import { RESTORE_DEFAULTS_BUTTON_LABEL } from '../../../constants';
|
import { RESTORE_DEFAULTS_BUTTON_LABEL } from '../../../constants';
|
||||||
|
import { AppSearchPageTemplate } from '../../layout';
|
||||||
import { MANAGE_CURATION_TITLE, RESTORE_CONFIRMATION } from '../constants';
|
import { MANAGE_CURATION_TITLE, RESTORE_CONFIRMATION } from '../constants';
|
||||||
|
import { getCurationsBreadcrumbs } from '../utils';
|
||||||
|
|
||||||
import { CurationLogic } from './curation_logic';
|
import { CurationLogic } from './curation_logic';
|
||||||
import { PromotedDocuments, OrganicDocuments, HiddenDocuments } from './documents';
|
import { PromotedDocuments, OrganicDocuments, HiddenDocuments } from './documents';
|
||||||
import { ActiveQuerySelect, ManageQueriesModal } from './queries';
|
import { ActiveQuerySelect, ManageQueriesModal } from './queries';
|
||||||
import { AddResultLogic, AddResultFlyout } from './results';
|
import { AddResultLogic, AddResultFlyout } from './results';
|
||||||
|
|
||||||
interface Props {
|
export const Curation: React.FC = () => {
|
||||||
curationsBreadcrumb: BreadcrumbTrail;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Curation: React.FC<Props> = ({ curationsBreadcrumb }) => {
|
|
||||||
const { curationId } = useParams() as { curationId: string };
|
const { curationId } = useParams() as { curationId: string };
|
||||||
const { loadCuration, resetCuration } = useActions(CurationLogic({ curationId }));
|
const { loadCuration, resetCuration } = useActions(CurationLogic({ curationId }));
|
||||||
const { dataLoading, queries } = useValues(CurationLogic({ curationId }));
|
const { dataLoading, queries } = useValues(CurationLogic({ curationId }));
|
||||||
|
@ -39,14 +32,12 @@ export const Curation: React.FC<Props> = ({ curationsBreadcrumb }) => {
|
||||||
loadCuration();
|
loadCuration();
|
||||||
}, [curationId]);
|
}, [curationId]);
|
||||||
|
|
||||||
if (dataLoading) return <Loading />;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AppSearchPageTemplate
|
||||||
<SetPageChrome trail={[...curationsBreadcrumb, queries.join(', ')]} />
|
pageChrome={getCurationsBreadcrumbs([queries.join(', ')])}
|
||||||
<EuiPageHeader
|
pageHeader={{
|
||||||
pageTitle={MANAGE_CURATION_TITLE}
|
pageTitle: MANAGE_CURATION_TITLE,
|
||||||
rightSideItems={[
|
rightSideItems: [
|
||||||
<EuiButton
|
<EuiButton
|
||||||
color="danger"
|
color="danger"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -55,10 +46,10 @@ export const Curation: React.FC<Props> = ({ curationsBreadcrumb }) => {
|
||||||
>
|
>
|
||||||
{RESTORE_DEFAULTS_BUTTON_LABEL}
|
{RESTORE_DEFAULTS_BUTTON_LABEL}
|
||||||
</EuiButton>,
|
</EuiButton>,
|
||||||
]}
|
],
|
||||||
responsive={false}
|
}}
|
||||||
/>
|
isLoading={dataLoading}
|
||||||
|
>
|
||||||
<EuiFlexGroup alignItems="flexEnd" gutterSize="xl" responsive={false}>
|
<EuiFlexGroup alignItems="flexEnd" gutterSize="xl" responsive={false}>
|
||||||
<EuiFlexItem>
|
<EuiFlexItem>
|
||||||
<ActiveQuerySelect />
|
<ActiveQuerySelect />
|
||||||
|
@ -69,7 +60,6 @@ export const Curation: React.FC<Props> = ({ curationsBreadcrumb }) => {
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
|
|
||||||
<EuiSpacer size="xl" />
|
<EuiSpacer size="xl" />
|
||||||
<FlashMessages />
|
|
||||||
|
|
||||||
<PromotedDocuments />
|
<PromotedDocuments />
|
||||||
<EuiSpacer />
|
<EuiSpacer />
|
||||||
|
@ -78,6 +68,6 @@ export const Curation: React.FC<Props> = ({ curationsBreadcrumb }) => {
|
||||||
<HiddenDocuments />
|
<HiddenDocuments />
|
||||||
|
|
||||||
{isFlyoutOpen && <AddResultFlyout />}
|
{isFlyoutOpen && <AddResultFlyout />}
|
||||||
</>
|
</AppSearchPageTemplate>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,7 +80,7 @@ export const HiddenDocuments: React.FC = () => {
|
||||||
<h3>
|
<h3>
|
||||||
{i18n.translate(
|
{i18n.translate(
|
||||||
'xpack.enterpriseSearch.appSearch.engine.curations.hiddenDocuments.emptyTitle',
|
'xpack.enterpriseSearch.appSearch.engine.curations.hiddenDocuments.emptyTitle',
|
||||||
{ defaultMessage: 'No documents are being hidden for this query' }
|
{ defaultMessage: "You haven't hidden any documents yet" }
|
||||||
)}
|
)}
|
||||||
</h3>
|
</h3>
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,6 @@ describe('CurationsRouter', () => {
|
||||||
const wrapper = shallow(<CurationsRouter />);
|
const wrapper = shallow(<CurationsRouter />);
|
||||||
|
|
||||||
expect(wrapper.find(Switch)).toHaveLength(1);
|
expect(wrapper.find(Switch)).toHaveLength(1);
|
||||||
expect(wrapper.find(Route)).toHaveLength(4);
|
expect(wrapper.find(Route)).toHaveLength(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,38 +8,26 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
import { APP_SEARCH_PLUGIN } from '../../../../../common/constants';
|
|
||||||
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
|
|
||||||
import { NotFound } from '../../../shared/not_found';
|
|
||||||
import {
|
import {
|
||||||
ENGINE_CURATIONS_PATH,
|
ENGINE_CURATIONS_PATH,
|
||||||
ENGINE_CURATIONS_NEW_PATH,
|
ENGINE_CURATIONS_NEW_PATH,
|
||||||
ENGINE_CURATION_PATH,
|
ENGINE_CURATION_PATH,
|
||||||
} from '../../routes';
|
} from '../../routes';
|
||||||
import { getEngineBreadcrumbs } from '../engine';
|
|
||||||
|
|
||||||
import { CURATIONS_TITLE, CREATE_NEW_CURATION_TITLE } from './constants';
|
|
||||||
import { Curation } from './curation';
|
import { Curation } from './curation';
|
||||||
import { Curations, CurationCreation } from './views';
|
import { Curations, CurationCreation } from './views';
|
||||||
|
|
||||||
export const CurationsRouter: React.FC = () => {
|
export const CurationsRouter: React.FC = () => {
|
||||||
const CURATIONS_BREADCRUMB = getEngineBreadcrumbs([CURATIONS_TITLE]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path={ENGINE_CURATIONS_PATH}>
|
<Route exact path={ENGINE_CURATIONS_PATH}>
|
||||||
<SetPageChrome trail={CURATIONS_BREADCRUMB} />
|
|
||||||
<Curations />
|
<Curations />
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path={ENGINE_CURATIONS_NEW_PATH}>
|
<Route exact path={ENGINE_CURATIONS_NEW_PATH}>
|
||||||
<SetPageChrome trail={[...CURATIONS_BREADCRUMB, CREATE_NEW_CURATION_TITLE]} />
|
|
||||||
<CurationCreation />
|
<CurationCreation />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={ENGINE_CURATION_PATH}>
|
<Route path={ENGINE_CURATION_PATH}>
|
||||||
<Curation curationsBreadcrumb={CURATIONS_BREADCRUMB} />
|
<Curation />
|
||||||
</Route>
|
|
||||||
<Route>
|
|
||||||
<NotFound breadcrumbs={CURATIONS_BREADCRUMB} product={APP_SEARCH_PLUGIN} />
|
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,21 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { convertToDate, addDocument, removeDocument } from './utils';
|
import '../../__mocks__/engine_logic.mock';
|
||||||
|
|
||||||
|
import { getCurationsBreadcrumbs, convertToDate, addDocument, removeDocument } from './utils';
|
||||||
|
|
||||||
|
describe('getCurationsBreadcrumbs', () => {
|
||||||
|
it('generates curation-prefixed breadcrumbs', () => {
|
||||||
|
expect(getCurationsBreadcrumbs()).toEqual(['Engines', 'some-engine', 'Curations']);
|
||||||
|
expect(getCurationsBreadcrumbs(['Some page'])).toEqual([
|
||||||
|
'Engines',
|
||||||
|
'some-engine',
|
||||||
|
'Curations',
|
||||||
|
'Some page',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('convertToDate', () => {
|
describe('convertToDate', () => {
|
||||||
it('converts the English-only server timestamps to a parseable Date', () => {
|
it('converts the English-only server timestamps to a parseable Date', () => {
|
||||||
|
|
|
@ -5,6 +5,14 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BreadcrumbTrail } from '../../../shared/kibana_chrome/generate_breadcrumbs';
|
||||||
|
import { getEngineBreadcrumbs } from '../engine';
|
||||||
|
|
||||||
|
import { CURATIONS_TITLE } from './constants';
|
||||||
|
|
||||||
|
export const getCurationsBreadcrumbs = (breadcrumbs: BreadcrumbTrail = []) =>
|
||||||
|
getEngineBreadcrumbs([CURATIONS_TITLE, ...breadcrumbs]);
|
||||||
|
|
||||||
// The server API feels us an English datestring, but we want to convert
|
// The server API feels us an English datestring, but we want to convert
|
||||||
// it to an actual Date() instance so that we can localize date formats.
|
// it to an actual Date() instance so that we can localize date formats.
|
||||||
export const convertToDate = (serverDateString: string): Date => {
|
export const convertToDate = (serverDateString: string): Date => {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setMockActions } from '../../../../__mocks__/kea_logic';
|
import { setMockActions } from '../../../../__mocks__/kea_logic';
|
||||||
|
import '../../../__mocks__/engine_logic.mock';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,10 @@ import React from 'react';
|
||||||
|
|
||||||
import { useActions } from 'kea';
|
import { useActions } from 'kea';
|
||||||
|
|
||||||
import { EuiPageHeader, EuiPageContent, EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
|
import { EuiPanel, EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { FlashMessages } from '../../../../shared/flash_messages';
|
import { AppSearchPageTemplate } from '../../layout';
|
||||||
import { MultiInputRows } from '../../multi_input_rows';
|
import { MultiInputRows } from '../../multi_input_rows';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -21,15 +21,17 @@ import {
|
||||||
QUERY_INPUTS_PLACEHOLDER,
|
QUERY_INPUTS_PLACEHOLDER,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { CurationsLogic } from '../index';
|
import { CurationsLogic } from '../index';
|
||||||
|
import { getCurationsBreadcrumbs } from '../utils';
|
||||||
|
|
||||||
export const CurationCreation: React.FC = () => {
|
export const CurationCreation: React.FC = () => {
|
||||||
const { createCuration } = useActions(CurationsLogic);
|
const { createCuration } = useActions(CurationsLogic);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AppSearchPageTemplate
|
||||||
<EuiPageHeader pageTitle={CREATE_NEW_CURATION_TITLE} />
|
pageChrome={getCurationsBreadcrumbs([CREATE_NEW_CURATION_TITLE])}
|
||||||
<FlashMessages />
|
pageHeader={{ pageTitle: CREATE_NEW_CURATION_TITLE }}
|
||||||
<EuiPageContent hasBorder>
|
>
|
||||||
|
<EuiPanel hasBorder>
|
||||||
<EuiTitle>
|
<EuiTitle>
|
||||||
<h2>
|
<h2>
|
||||||
{i18n.translate(
|
{i18n.translate(
|
||||||
|
@ -56,7 +58,7 @@ export const CurationCreation: React.FC = () => {
|
||||||
inputPlaceholder={QUERY_INPUTS_PLACEHOLDER}
|
inputPlaceholder={QUERY_INPUTS_PLACEHOLDER}
|
||||||
onSubmit={(queries) => createCuration(queries)}
|
onSubmit={(queries) => createCuration(queries)}
|
||||||
/>
|
/>
|
||||||
</EuiPageContent>
|
</EuiPanel>
|
||||||
</>
|
</AppSearchPageTemplate>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,17 +6,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__/kea_logic';
|
import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__/kea_logic';
|
||||||
|
import '../../../../__mocks__/react_router';
|
||||||
import '../../../__mocks__/engine_logic.mock';
|
import '../../../__mocks__/engine_logic.mock';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { shallow, ReactWrapper } from 'enzyme';
|
import { shallow, ReactWrapper } from 'enzyme';
|
||||||
|
|
||||||
import { EuiPageHeader, EuiBasicTable } from '@elastic/eui';
|
import { EuiBasicTable } from '@elastic/eui';
|
||||||
|
|
||||||
import { Loading } from '../../../../shared/loading';
|
import { mountWithIntl, getPageTitle } from '../../../../test_helpers';
|
||||||
import { mountWithIntl } from '../../../../test_helpers';
|
|
||||||
import { EmptyState } from '../components';
|
|
||||||
|
|
||||||
import { Curations, CurationsTable } from './curations';
|
import { Curations, CurationsTable } from './curations';
|
||||||
|
|
||||||
|
@ -61,32 +60,34 @@ describe('Curations', () => {
|
||||||
it('renders', () => {
|
it('renders', () => {
|
||||||
const wrapper = shallow(<Curations />);
|
const wrapper = shallow(<Curations />);
|
||||||
|
|
||||||
expect(wrapper.find(EuiPageHeader).prop('pageTitle')).toEqual('Curated results');
|
expect(getPageTitle(wrapper)).toEqual('Curated results');
|
||||||
expect(wrapper.find(CurationsTable)).toHaveLength(1);
|
expect(wrapper.find(CurationsTable)).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a loading component on page load', () => {
|
describe('loading state', () => {
|
||||||
setMockValues({ ...values, dataLoading: true, curations: [] });
|
it('renders a full-page loading state on initial page load', () => {
|
||||||
const wrapper = shallow(<Curations />);
|
setMockValues({ ...values, dataLoading: true, curations: [] });
|
||||||
|
const wrapper = shallow(<Curations />);
|
||||||
|
|
||||||
expect(wrapper.find(Loading)).toHaveLength(1);
|
expect(wrapper.prop('isLoading')).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not re-render a full-page loading state after initial page load (uses component-level loading state instead)', () => {
|
||||||
|
setMockValues({ ...values, dataLoading: true, curations: [{}] });
|
||||||
|
const wrapper = shallow(<Curations />);
|
||||||
|
|
||||||
|
expect(wrapper.prop('isLoading')).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls loadCurations on page load', () => {
|
it('calls loadCurations on page load', () => {
|
||||||
|
setMockValues({ ...values, myRole: {} }); // Required for AppSearchPageTemplate to load
|
||||||
mountWithIntl(<Curations />);
|
mountWithIntl(<Curations />);
|
||||||
|
|
||||||
expect(actions.loadCurations).toHaveBeenCalledTimes(1);
|
expect(actions.loadCurations).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('CurationsTable', () => {
|
describe('CurationsTable', () => {
|
||||||
it('renders an empty state', () => {
|
|
||||||
setMockValues({ ...values, curations: [] });
|
|
||||||
const table = shallow(<CurationsTable />).find(EuiBasicTable);
|
|
||||||
const noItemsMessage = table.prop('noItemsMessage') as React.ReactElement;
|
|
||||||
|
|
||||||
expect(noItemsMessage.type).toEqual(EmptyState);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('passes loading prop based on dataLoading', () => {
|
it('passes loading prop based on dataLoading', () => {
|
||||||
setMockValues({ ...values, dataLoading: true });
|
setMockValues({ ...values, dataLoading: true });
|
||||||
const wrapper = shallow(<CurationsTable />);
|
const wrapper = shallow(<CurationsTable />);
|
||||||
|
|
|
@ -9,25 +9,24 @@ import React, { useEffect } from 'react';
|
||||||
|
|
||||||
import { useValues, useActions } from 'kea';
|
import { useValues, useActions } from 'kea';
|
||||||
|
|
||||||
import { EuiBasicTable, EuiBasicTableColumn, EuiPageContent, EuiPageHeader } from '@elastic/eui';
|
import { EuiBasicTable, EuiBasicTableColumn, EuiPanel } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { EDIT_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants';
|
import { EDIT_BUTTON_LABEL, DELETE_BUTTON_LABEL } from '../../../../shared/constants';
|
||||||
import { FlashMessages } from '../../../../shared/flash_messages';
|
|
||||||
import { KibanaLogic } from '../../../../shared/kibana';
|
import { KibanaLogic } from '../../../../shared/kibana';
|
||||||
import { Loading } from '../../../../shared/loading';
|
|
||||||
import { EuiButtonTo, EuiLinkTo } from '../../../../shared/react_router_helpers';
|
import { EuiButtonTo, EuiLinkTo } from '../../../../shared/react_router_helpers';
|
||||||
import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination';
|
import { convertMetaToPagination, handlePageChange } from '../../../../shared/table_pagination';
|
||||||
|
|
||||||
import { ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH } from '../../../routes';
|
import { ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH } from '../../../routes';
|
||||||
import { FormattedDateTime } from '../../../utils/formatted_date_time';
|
import { FormattedDateTime } from '../../../utils/formatted_date_time';
|
||||||
import { generateEnginePath } from '../../engine';
|
import { generateEnginePath } from '../../engine';
|
||||||
|
import { AppSearchPageTemplate } from '../../layout';
|
||||||
|
|
||||||
import { EmptyState } from '../components';
|
import { EmptyState } from '../components';
|
||||||
import { CURATIONS_OVERVIEW_TITLE, CREATE_NEW_CURATION_TITLE } from '../constants';
|
import { CURATIONS_OVERVIEW_TITLE, CREATE_NEW_CURATION_TITLE } from '../constants';
|
||||||
import { CurationsLogic } from '../curations_logic';
|
import { CurationsLogic } from '../curations_logic';
|
||||||
import { Curation } from '../types';
|
import { Curation } from '../types';
|
||||||
import { convertToDate } from '../utils';
|
import { getCurationsBreadcrumbs, convertToDate } from '../utils';
|
||||||
|
|
||||||
export const Curations: React.FC = () => {
|
export const Curations: React.FC = () => {
|
||||||
const { dataLoading, curations, meta } = useValues(CurationsLogic);
|
const { dataLoading, curations, meta } = useValues(CurationsLogic);
|
||||||
|
@ -37,23 +36,29 @@ export const Curations: React.FC = () => {
|
||||||
loadCurations();
|
loadCurations();
|
||||||
}, [meta.page.current]);
|
}, [meta.page.current]);
|
||||||
|
|
||||||
if (dataLoading && !curations.length) return <Loading />;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AppSearchPageTemplate
|
||||||
<EuiPageHeader
|
pageChrome={getCurationsBreadcrumbs()}
|
||||||
pageTitle={CURATIONS_OVERVIEW_TITLE}
|
pageHeader={{
|
||||||
rightSideItems={[
|
pageTitle: CURATIONS_OVERVIEW_TITLE,
|
||||||
<EuiButtonTo to={generateEnginePath(ENGINE_CURATIONS_NEW_PATH)} fill>
|
rightSideItems: [
|
||||||
|
<EuiButtonTo
|
||||||
|
to={generateEnginePath(ENGINE_CURATIONS_NEW_PATH)}
|
||||||
|
iconType="plusInCircle"
|
||||||
|
fill
|
||||||
|
>
|
||||||
{CREATE_NEW_CURATION_TITLE}
|
{CREATE_NEW_CURATION_TITLE}
|
||||||
</EuiButtonTo>,
|
</EuiButtonTo>,
|
||||||
]}
|
],
|
||||||
/>
|
}}
|
||||||
<EuiPageContent hasBorder>
|
isLoading={dataLoading && !curations.length}
|
||||||
<FlashMessages />
|
isEmptyState={!curations.length}
|
||||||
|
emptyState={<EmptyState />}
|
||||||
|
>
|
||||||
|
<EuiPanel hasBorder>
|
||||||
<CurationsTable />
|
<CurationsTable />
|
||||||
</EuiPageContent>
|
</EuiPanel>
|
||||||
</>
|
</AppSearchPageTemplate>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -139,7 +144,6 @@ export const CurationsTable: React.FC = () => {
|
||||||
responsive
|
responsive
|
||||||
hasActions
|
hasActions
|
||||||
loading={dataLoading}
|
loading={dataLoading}
|
||||||
noItemsMessage={<EmptyState />}
|
|
||||||
pagination={{
|
pagination={{
|
||||||
...convertMetaToPagination(meta),
|
...convertMetaToPagination(meta),
|
||||||
hidePerPageOptions: true,
|
hidePerPageOptions: true,
|
||||||
|
|
|
@ -129,6 +129,11 @@ export const EngineRouter: React.FC = () => {
|
||||||
<RelevanceTuning />
|
<RelevanceTuning />
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
)}
|
||||||
|
{canManageEngineCurations && (
|
||||||
|
<Route path={ENGINE_CURATIONS_PATH}>
|
||||||
|
<CurationsRouter />
|
||||||
|
</Route>
|
||||||
|
)}
|
||||||
{canManageEngineResultSettings && (
|
{canManageEngineResultSettings && (
|
||||||
<Route path={ENGINE_RESULT_SETTINGS_PATH}>
|
<Route path={ENGINE_RESULT_SETTINGS_PATH}>
|
||||||
<ResultSettings />
|
<ResultSettings />
|
||||||
|
@ -146,11 +151,6 @@ export const EngineRouter: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
{/* TODO: Remove layout once page template migration is over */}
|
{/* TODO: Remove layout once page template migration is over */}
|
||||||
<Layout navigation={<AppSearchNav />}>
|
<Layout navigation={<AppSearchNav />}>
|
||||||
{canManageEngineCurations && (
|
|
||||||
<Route path={ENGINE_CURATIONS_PATH}>
|
|
||||||
<CurationsRouter />
|
|
||||||
</Route>
|
|
||||||
)}
|
|
||||||
{canManageEngineSynonyms && (
|
{canManageEngineSynonyms && (
|
||||||
<Route path={ENGINE_SYNONYMS_PATH}>
|
<Route path={ENGINE_SYNONYMS_PATH}>
|
||||||
<Synonyms />
|
<Synonyms />
|
||||||
|
|
Loading…
Reference in a new issue