mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[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:
parent
ea229891f1
commit
aa2427b3c0
11 changed files with 154 additions and 32 deletions
|
@ -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', () => {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
*/
|
||||
|
||||
export { SetupGuide } from './setup_guide';
|
||||
export { SetupGuideCta } from './setup_guide_cta';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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>
|
||||
);
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue