[Enterprise Search] Overview - add missing setup guide CTAs (#81420)

* Misc enterprise search overview cleanup

- remove extra comment block
- tests
  - use setMockValues
  - remove unused EuiPage
  - error connecting test - clarify assertion

* Add Setup Guide CTA component

* Update Product Selector to show Setup Guide CTAs

+ tests clean up - use setMockValues, import reorder

* Add EuiPanel RR link to setup guide
This commit is contained in:
Constance 2020-10-22 08:50:54 -07:00 committed by GitHub
parent ea229891f1
commit aa2427b3c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 154 additions and 32 deletions

View file

@ -4,28 +4,30 @@
* you may not use this file except in compliance with the Elastic License.
*/
import '../../../__mocks__/kea.mock';
import { setMockValues } from '../../../__mocks__/kea.mock';
import React from 'react';
import { useValues } from 'kea';
import { shallow } from 'enzyme';
import { EuiPage } from '@elastic/eui';
import { ProductSelector } from './';
import { SetupGuideCta } from '../setup_guide';
import { ProductCard } from '../product_card';
import { ProductSelector } from './';
describe('ProductSelector', () => {
it('renders the overview page and product cards with no host set', () => {
(useValues as jest.Mock).mockImplementationOnce(() => ({ config: { host: '' } }));
it('renders the overview page, product cards, & setup guide CTAs with no host set', () => {
setMockValues({ config: { host: '' } });
const wrapper = shallow(<ProductSelector access={{}} />);
expect(wrapper.find(EuiPage).hasClass('enterpriseSearchOverview')).toBe(true);
expect(wrapper.find(ProductCard)).toHaveLength(2);
expect(wrapper.find(SetupGuideCta)).toHaveLength(1);
});
describe('access checks when host is set', () => {
beforeEach(() => {
(useValues as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'localhost' } }));
setMockValues({ config: { host: 'localhost' } });
});
it('does not render the App Search card if the user does not have access to AS', () => {

View file

@ -3,11 +3,6 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/*
* 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 { useValues } from 'kea';
@ -30,6 +25,7 @@ import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kiba
import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
import { ProductCard } from '../product_card';
import { SetupGuideCta } from '../setup_guide';
import AppSearchImage from '../../assets/app_search.png';
import WorkplaceSearchImage from '../../assets/workplace_search.png';
@ -66,9 +62,13 @@ export const ProductSelector: React.FC<IProductSelectorProps> = ({ access }) =>
</EuiTitle>
<EuiTitle size="s">
<p className="enterpriseSearchOverview__subheading">
{i18n.translate('xpack.enterpriseSearch.overview.subheading', {
defaultMessage: 'Select a product to get started',
})}
{config.host
? i18n.translate('xpack.enterpriseSearch.overview.subheading', {
defaultMessage: 'Select a product to get started.',
})
: i18n.translate('xpack.enterpriseSearch.overview.setupHeading', {
defaultMessage: 'Choose a product to set up and get started.',
})}
</p>
</EuiTitle>
</EuiPageHeaderSection>
@ -87,6 +87,7 @@ export const ProductSelector: React.FC<IProductSelectorProps> = ({ access }) =>
)}
</EuiFlexGroup>
<EuiSpacer />
{!config.host && <SetupGuideCta />}
</EuiPageContentBody>
</EuiPageBody>
</EuiPage>

View file

@ -5,3 +5,4 @@
*/
export { SetupGuide } from './setup_guide';
export { SetupGuideCta } from './setup_guide_cta';

View file

@ -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.
*/
.enterpriseSearchSetupCta {
margin: $euiSize auto $euiSizeXL;
// Clickable EuiPanel override - line panel up with product cards
&.euiPanel--isClickable {
width: calc(100% - #{$euiSize});
}
&__text {
max-width: $euiSize * 40;
}
&__image {
display: block;
max-width: 100%;
width: $euiSize * 10;
margin: 0 auto;
@include euiBreakpoint('xs', 's') {
width: $euiSize * 15;
}
}
}

View file

@ -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.
*/
import React from 'react';
import { shallow } from 'enzyme';
import { SetupGuideCta } from './';
describe('SetupGuideCta', () => {
it('renders', () => {
const wrapper = shallow(<SetupGuideCta />);
expect(wrapper.find('.enterpriseSearchSetupCta')).toHaveLength(1);
expect(wrapper.find('img')).toHaveLength(1);
});
});

View file

@ -0,0 +1,38 @@
/*
* 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, EuiFlexItem, EuiTitle, EuiText } from '@elastic/eui';
import { EuiPanel } from '../../../shared/react_router_helpers';
import CtaImage from './assets/getting_started.png';
import './setup_guide_cta.scss';
export const SetupGuideCta: React.FC = () => (
<EuiPanel to="/setup_guide" paddingSize="l" className="enterpriseSearchSetupCta">
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem className="enterpriseSearchSetupCta__text">
<EuiTitle size="s">
<h2>
{i18n.translate('xpack.enterpriseSearch.overview.setupCta.title', {
defaultMessage: 'Enterprise-grade functionality for teams big and small',
})}
</h2>
</EuiTitle>
<EuiText size="s" color="subdued">
{i18n.translate('xpack.enterpriseSearch.overview.setupCta.description', {
defaultMessage:
'Add search to your app or internal organization with Elastic App Search and Workplace Search. Watch the video to see what you can do when search is made easy.',
})}
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<img src={CtaImage} alt="" className="enterpriseSearchSetupCta__image" />
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);

View file

@ -6,10 +6,8 @@
import React from 'react';
import { shallow } from 'enzyme';
import { EuiPage } from '@elastic/eui';
import '../__mocks__/kea.mock';
import { useValues } from 'kea';
import { setMockValues } from '../__mocks__/kea.mock';
import { EnterpriseSearch } from './';
import { SetupGuide } from './components/setup_guide';
@ -18,7 +16,7 @@ import { ProductSelector } from './components/product_selector';
describe('EnterpriseSearch', () => {
it('renders the Setup Guide and Product Selector', () => {
(useValues as jest.Mock).mockReturnValue({
setMockValues({
errorConnecting: false,
config: { host: 'localhost' },
});
@ -28,15 +26,23 @@ describe('EnterpriseSearch', () => {
expect(wrapper.find(ProductSelector)).toHaveLength(1);
});
it('renders the error connecting prompt when host is not configured', () => {
(useValues as jest.Mock).mockReturnValueOnce({
it('renders the error connecting prompt only if host is configured', () => {
setMockValues({
errorConnecting: true,
config: { host: '' },
config: { host: 'localhost' },
});
const wrapper = shallow(<EnterpriseSearch />);
expect(wrapper.find(ErrorConnecting)).toHaveLength(1);
expect(wrapper.find(EuiPage)).toHaveLength(0);
expect(wrapper.find(ProductSelector)).toHaveLength(0);
setMockValues({
errorConnecting: true,
config: { host: '' },
});
wrapper.setProps({}); // Re-render
expect(wrapper.find(ErrorConnecting)).toHaveLength(0);
expect(wrapper.find(ProductSelector)).toHaveLength(1);
});
});

View file

@ -25,7 +25,7 @@ export const EnterpriseSearch: React.FC<IInitialAppData> = ({ access = {} }) =>
const { errorConnecting } = useValues(HttpLogic);
const { config } = useValues(KibanaLogic);
const showErrorConnecting = config.host && errorConnecting;
const showErrorConnecting = !!(config.host && errorConnecting);
return (
<Switch>

View file

@ -8,18 +8,18 @@ import '../../__mocks__/kea.mock';
import React from 'react';
import { shallow, mount } from 'enzyme';
import { EuiLink, EuiButton } from '@elastic/eui';
import { EuiLink, EuiButton, EuiPanel } from '@elastic/eui';
import { mockKibanaValues, mockHistory } from '../../__mocks__';
import { EuiReactRouterLink, EuiReactRouterButton } from './eui_link';
import { EuiReactRouterLink, EuiReactRouterButton, EuiReactRouterPanel } from './eui_link';
describe('EUI & React Router Component Helpers', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('renders', () => {
it('renders an EuiLink', () => {
const wrapper = shallow(<EuiReactRouterLink to="/" />);
expect(wrapper.find(EuiLink)).toHaveLength(1);
@ -31,6 +31,13 @@ describe('EUI & React Router Component Helpers', () => {
expect(wrapper.find(EuiButton)).toHaveLength(1);
});
it('renders an EuiPanel', () => {
const wrapper = shallow(<EuiReactRouterPanel to="/" paddingSize="l" />);
expect(wrapper.find(EuiPanel)).toHaveLength(1);
expect(wrapper.find(EuiPanel).prop('paddingSize')).toEqual('l');
});
it('passes down all ...rest props', () => {
const wrapper = shallow(<EuiReactRouterLink to="/" data-test-subj="foo" external={true} />);
const link = wrapper.find(EuiLink);

View file

@ -6,14 +6,15 @@
import React from 'react';
import { useValues } from 'kea';
import { EuiLink, EuiButton, EuiButtonProps, EuiLinkAnchorProps } from '@elastic/eui';
import { EuiLink, EuiButton, EuiButtonProps, EuiLinkAnchorProps, EuiPanel } from '@elastic/eui';
import { EuiPanelProps } from '@elastic/eui/src/components/panel/panel';
import { KibanaLogic } from '../kibana';
import { HttpLogic } from '../http';
import { letBrowserHandleEvent, createHref } from './';
/**
* Generates either an EuiLink or EuiButton with a React-Router-ified link
* Generates EUI components with React-Router-ified links
*
* Based off of EUI's recommendations for handling React Router:
* https://github.com/elastic/eui/blob/master/wiki/react-router.md#react-router-51
@ -54,9 +55,11 @@ export const EuiReactRouterHelper: React.FC<IEuiReactRouterProps> = ({
return React.cloneElement(children as React.ReactElement, reactRouterProps);
};
type TEuiReactRouterLinkProps = EuiLinkAnchorProps & IEuiReactRouterProps;
type TEuiReactRouterButtonProps = EuiButtonProps & IEuiReactRouterProps;
/**
* Component helpers
*/
type TEuiReactRouterLinkProps = EuiLinkAnchorProps & IEuiReactRouterProps;
export const EuiReactRouterLink: React.FC<TEuiReactRouterLinkProps> = ({
to,
onClick,
@ -68,6 +71,7 @@ export const EuiReactRouterLink: React.FC<TEuiReactRouterLinkProps> = ({
</EuiReactRouterHelper>
);
type TEuiReactRouterButtonProps = EuiButtonProps & IEuiReactRouterProps;
export const EuiReactRouterButton: React.FC<TEuiReactRouterButtonProps> = ({
to,
onClick,
@ -78,3 +82,15 @@ export const EuiReactRouterButton: React.FC<TEuiReactRouterButtonProps> = ({
<EuiButton {...rest} />
</EuiReactRouterHelper>
);
type TEuiReactRouterPanelProps = EuiPanelProps & IEuiReactRouterProps;
export const EuiReactRouterPanel: React.FC<TEuiReactRouterPanelProps> = ({
to,
onClick,
shouldNotCreateHref,
...rest
}) => (
<EuiReactRouterHelper {...{ to, onClick, shouldNotCreateHref }}>
<EuiPanel {...rest} />
</EuiReactRouterHelper>
);

View file

@ -6,5 +6,8 @@
export { letBrowserHandleEvent } from './link_events';
export { createHref, ICreateHrefOptions } from './create_href';
export { EuiReactRouterLink as EuiLink } from './eui_link';
export { EuiReactRouterButton as EuiButton } from './eui_link';
export {
EuiReactRouterLink as EuiLink,
EuiReactRouterButton as EuiButton,
EuiReactRouterPanel as EuiPanel,
} from './eui_link';