Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workplace Search] Convert Sources pages to new page template (+ personal dashboard) #102592

Merged
merged 15 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import '../../../../__mocks__/shallow_useeffect.mock';

import { setMockValues } from '../../../../__mocks__/kea_logic';
import { contentSources } from '../../../__mocks__/content_sources.mock';

import React from 'react';

import { shallow } from 'enzyme';

import { EuiCallOut } from '@elastic/eui';

import { WorkplaceSearchPageTemplate, PersonalDashboardLayout } from '../../../components/layout';

import { SourceInfoCard } from './source_info_card';
import { SourceLayout } from './source_layout';

describe('SourceLayout', () => {
const contentSource = contentSources[1];
const mockValues = {
contentSource,
dataLoading: false,
isOrganization: true,
};

beforeEach(() => {
setMockValues({ ...mockValues });
});

it('renders', () => {
const wrapper = shallow(
<SourceLayout>
<div className="testChild" />
</SourceLayout>
);

expect(wrapper.find(SourceInfoCard)).toHaveLength(1);
expect(wrapper.find('.testChild')).toHaveLength(1);
});

it('renders the default Workplace Search layout when on an organization view', () => {
setMockValues({ ...mockValues, isOrganization: true });
const wrapper = shallow(<SourceLayout />);

expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate);
});

it('renders a personal dashboard layout when not on an organization view', () => {
setMockValues({ ...mockValues, isOrganization: false });
const wrapper = shallow(<SourceLayout />);

expect(wrapper.type()).toEqual(PersonalDashboardLayout);
});

it('passes any page template props to the underlying page template', () => {
const wrapper = shallow(<SourceLayout pageViewTelemetry="test" />);

expect(wrapper.find(WorkplaceSearchPageTemplate).prop('pageViewTelemetry')).toEqual('test');
});

it('handles breadcrumbs while loading', () => {
setMockValues({
...mockValues,
contentSource: {},
dataLoading: true,
});
const wrapper = shallow(<SourceLayout />);

expect(wrapper.prop('pageChrome')).toEqual(['Sources', '...']);
});

it('renders a callout when the source is not supported by the current license', () => {
setMockValues({ ...mockValues, contentSource: { supportedByLicense: false } });
const wrapper = shallow(<SourceLayout />);

expect(wrapper.find(EuiCallOut)).toHaveLength(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';

import { useValues } from 'kea';
import moment from 'moment';

import { EuiButton, EuiCallOut, EuiHorizontalRule, EuiSpacer } from '@elastic/eui';

import { PageTemplateProps } from '../../../../shared/layout';
import { AppLogic } from '../../../app_logic';
import { WorkplaceSearchPageTemplate, PersonalDashboardLayout } from '../../../components/layout';
import { NAV } from '../../../constants';
import { ENT_SEARCH_LICENSE_MANAGEMENT } from '../../../routes';

import {
SOURCE_DISABLED_CALLOUT_TITLE,
SOURCE_DISABLED_CALLOUT_DESCRIPTION,
SOURCE_DISABLED_CALLOUT_BUTTON,
} from '../constants';
import { SourceLogic } from '../source_logic';

import { SourceInfoCard } from './source_info_card';

export const SourceLayout: React.FC<PageTemplateProps> = ({
children,
pageChrome = [],
...props
}) => {
yakhinvadim marked this conversation as resolved.
Show resolved Hide resolved
const { contentSource, dataLoading } = useValues(SourceLogic);
const { isOrganization } = useValues(AppLogic);

const {
name,
createdAt,
serviceType,
serviceName,
isFederatedSource,
supportedByLicense,
} = contentSource;

const pageHeader = (
<>
<SourceInfoCard
sourceName={serviceName}
sourceType={serviceType}
dateCreated={moment(createdAt).format('MMMM D, YYYY')}
isFederatedSource={isFederatedSource}
/>
<EuiHorizontalRule />
</>
);

const callout = (
<>
<EuiCallOut title={SOURCE_DISABLED_CALLOUT_TITLE} color="warning" iconType="alert">
<p>{SOURCE_DISABLED_CALLOUT_DESCRIPTION}</p>
<EuiButton color="warning" href={ENT_SEARCH_LICENSE_MANAGEMENT}>
{SOURCE_DISABLED_CALLOUT_BUTTON}
</EuiButton>
</EuiCallOut>
<EuiSpacer />
</>
);

const Layout = isOrganization ? WorkplaceSearchPageTemplate : PersonalDashboardLayout;

return (
<Layout
isLoading={dataLoading}
{...props}
pageChrome={[NAV.SOURCES, name || '...', ...pageChrome]}
>
{!supportedByLicense && callout}
{pageHeader}
{children}
</Layout>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@
* 2.0.
*/

import '../../../__mocks__/shallow_useeffect.mock';

import { setMockValues, setMockActions } from '../../../__mocks__/kea_logic';
import { mockLocation, mockUseParams } from '../../../__mocks__/react_router';
import { mockUseParams } from '../../../__mocks__/react_router';
import { unmountHandler } from '../../../__mocks__/shallow_useeffect.mock';
import { contentSources } from '../../__mocks__/content_sources.mock';

import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { Route } from 'react-router-dom';

import { shallow } from 'enzyme';

import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { Loading } from '../../../shared/loading';
import { NAV } from '../../constants';
import { WorkplaceSearchPageTemplate, PersonalDashboardLayout } from '../../components/layout';

import { DisplaySettingsRouter } from './components/display_settings';
import { Overview } from './components/overview';
Expand All @@ -37,6 +33,7 @@ describe('SourceRouter', () => {
const mockValues = {
contentSource,
dataLoading: false,
isOrganization: true,
};

beforeEach(() => {
Expand All @@ -50,11 +47,41 @@ describe('SourceRouter', () => {
}));
});

it('returns Loading when loading', () => {
setMockValues({ ...mockValues, dataLoading: true });
const wrapper = shallow(<SourceRouter />);
describe('mount/unmount events', () => {
it('fetches & initializes source data on mount', () => {
shallow(<SourceRouter />);

expect(wrapper.find(Loading)).toHaveLength(1);
expect(initializeSource).toHaveBeenCalledWith(contentSource.id);
});

it('resets state on unmount', () => {
shallow(<SourceRouter />);
unmountHandler();

expect(resetSourceState).toHaveBeenCalled();
});
});

describe('loading state when fetching source data', () => {
// NOTE: The early page isLoading returns are required to prevent a flash of a completely empty
// page (instead of preserving the layout/side nav while loading). We also cannot let the code
// fall through to the router because some routes are conditionally rendered based on isCustomSource.

it('returns an empty loading Workplace Search page on organization views', () => {
setMockValues({ ...mockValues, dataLoading: true, isOrganization: true });
const wrapper = shallow(<SourceRouter />);

expect(wrapper.type()).toEqual(WorkplaceSearchPageTemplate);
expect(wrapper.prop('isLoading')).toEqual(true);
});

it('returns an empty loading personal dashboard page when not on an organization view', () => {
setMockValues({ ...mockValues, dataLoading: true, isOrganization: false });
const wrapper = shallow(<SourceRouter />);

expect(wrapper.type()).toEqual(PersonalDashboardLayout);
expect(wrapper.prop('isLoading')).toEqual(true);
});
});

it('renders source routes (standard)', () => {
Expand All @@ -63,7 +90,6 @@ describe('SourceRouter', () => {
expect(wrapper.find(Overview)).toHaveLength(1);
expect(wrapper.find(SourceSettings)).toHaveLength(1);
expect(wrapper.find(SourceContent)).toHaveLength(1);
expect(wrapper.find(Switch)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(3);
});

Expand All @@ -76,55 +102,4 @@ describe('SourceRouter', () => {
expect(wrapper.find(SchemaChangeErrors)).toHaveLength(1);
expect(wrapper.find(Route)).toHaveLength(6);
});

it('handles breadcrumbs while loading (standard)', () => {
setMockValues({
...mockValues,
contentSource: {},
});

const loadingBreadcrumbs = ['Sources', '...'];

const wrapper = shallow(<SourceRouter />);

const overviewBreadCrumb = wrapper.find(SetPageChrome).at(0);
const contentBreadCrumb = wrapper.find(SetPageChrome).at(1);
const settingsBreadCrumb = wrapper.find(SetPageChrome).at(2);

expect(overviewBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs]);
expect(contentBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.CONTENT]);
expect(settingsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SETTINGS]);
});

it('handles breadcrumbs while loading (custom)', () => {
setMockValues({
...mockValues,
contentSource: { serviceType: 'custom' },
});

const loadingBreadcrumbs = ['Sources', '...'];

const wrapper = shallow(<SourceRouter />);

const schemaBreadCrumb = wrapper.find(SetPageChrome).at(2);
const schemaErrorsBreadCrumb = wrapper.find(SetPageChrome).at(3);
const displaySettingsBreadCrumb = wrapper.find(SetPageChrome).at(4);

expect(schemaBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]);
expect(schemaErrorsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]);
expect(displaySettingsBreadCrumb.prop('trail')).toEqual([
...loadingBreadcrumbs,
NAV.DISPLAY_SETTINGS,
]);
});

describe('reset state', () => {
it('resets state when leaving source tree', () => {
mockLocation.pathname = '/home';
shallow(<SourceRouter />);
unmountHandler();

expect(resetSourceState).toHaveBeenCalled();
});
});
});
Loading