Filter guided onboarding solutions based on cloud discovery questions… (#153367)

Related to #148911

This PR updates the guided onboarding landing page to filter solutions
based on user selected use case in cloud discovery questions. The value
will be passed as querystring parameter `?cloudDiscoveryUseCase=[value]`
from Cloud UI.

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
claracruz 2023-04-04 12:15:42 +01:00 committed by GitHub
parent d718066d75
commit cb3a42a880
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 86 deletions

View file

@ -10,6 +10,7 @@ import React from 'react';
import { EuiButton, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react';
import { ApplicationStart } from '@kbn/core-application-browser';
import { GuideCardSolutions } from './guide_cards';
const filterButtonCss = css`
@ -27,22 +28,34 @@ const filterButtonCss = css`
}
`;
export type GuideFilterValues = GuideCardSolutions | 'all';
interface GuideFiltersProps {
export interface GuideFiltersProps {
activeFilter: GuideFilterValues;
setActiveFilter: React.Dispatch<React.SetStateAction<GuideFilterValues>>;
application: ApplicationStart;
}
export const GuideFilters = ({ activeFilter, setActiveFilter }: GuideFiltersProps) => {
export const GuideFilters = ({ activeFilter, setActiveFilter, application }: GuideFiltersProps) => {
const { euiTheme } = useEuiTheme();
const activeFilterFill = css`
background: ${euiTheme.colors.darkestShade};
color: ${euiTheme.colors.lightestShade};
`;
const setQuerystringParams = ({ useCase }: { useCase: string }) => {
application.navigateToApp('home', { path: `#/getting_started?useCase=${useCase}` });
};
const onSelectFilter = (e: React.BaseSyntheticEvent) => {
const {
currentTarget: { dataset },
} = e;
setQuerystringParams({ useCase: dataset.filterId });
setActiveFilter(dataset.filterId);
};
return (
<EuiFlexGroup justifyContent="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => setActiveFilter('all')}
onClick={onSelectFilter}
data-filter-id="all"
color="text"
css={[filterButtonCss, activeFilter === 'all' && activeFilterFill]}
>
@ -54,7 +67,8 @@ export const GuideFilters = ({ activeFilter, setActiveFilter }: GuideFiltersProp
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => setActiveFilter('search')}
onClick={onSelectFilter}
data-filter-id="search"
color="text"
css={[filterButtonCss, activeFilter === 'search' && activeFilterFill]}
>
@ -66,7 +80,8 @@ export const GuideFilters = ({ activeFilter, setActiveFilter }: GuideFiltersProp
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => setActiveFilter('observability')}
onClick={onSelectFilter}
data-filter-id="observability"
color="text"
css={[filterButtonCss, activeFilter === 'observability' && activeFilterFill]}
>
@ -78,7 +93,8 @@ export const GuideFilters = ({ activeFilter, setActiveFilter }: GuideFiltersProp
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
onClick={() => setActiveFilter('security')}
onClick={onSelectFilter}
data-filter-id="security"
color="text"
css={[filterButtonCss, activeFilter === 'security' && activeFilterFill]}
>

View file

@ -1,74 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`getting started should render getting started component 1`] = `
<_KibanaPageTemplate
grow={true}
panelled={false}
>
<_EuiPageSection
alignment="center"
css={
Object {
"map": undefined,
"name": "hdi05h",
"next": undefined,
"styles": "
padding: calc(16px*3) calc(16px*4);
",
"toString": [Function],
}
}
data-test-subj="onboarding--landing-page"
>
<EuiTitle
className="eui-textCenter"
size="l"
>
<h1>
What would you like to do first?
</h1>
</EuiTitle>
<EuiSpacer
size="s"
/>
<EuiText
size="m"
textAlign="center"
>
<p>
Select an option and we'll help you get started.
</p>
</EuiText>
<EuiSpacer
size="s"
/>
<EuiSpacer
size="xxl"
/>
<GuideFilters
activeFilter="all"
setActiveFilter={[Function]}
/>
<EuiSpacer
size="xxl"
/>
<GuideCards
activateGuide={[Function]}
activeFilter="all"
guidesState={Array []}
navigateToApp={[MockFunction]}
/>
<EuiSpacer />
<div
className="eui-textCenter"
>
<EuiLink
data-test-subj="onboarding--skipGuideLink"
onClick={[Function]}
>
Id like to do something else.
</EuiLink>
</div>
</_EuiPageSection>
</_KibanaPageTemplate>
`;

View file

@ -9,13 +9,16 @@
import React from 'react';
import { shallow } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { findTestSubject, registerTestBed, TestBed } from '@kbn/test-jest-helpers';
import { findTestSubject, registerTestBed, TestBed, mountWithIntl } from '@kbn/test-jest-helpers';
import { MemoryRouter } from 'react-router-dom';
import { cloudMock } from '@kbn/cloud-plugin/public/mocks';
import { chromeServiceMock, applicationServiceMock, httpServiceMock } from '@kbn/core/public/mocks';
import { ApiService } from '@kbn/guided-onboarding-plugin/public/services/api.service';
import { GettingStarted } from './getting_started';
import { KEY_ENABLE_WELCOME } from '../home';
import { GuideFiltersProps } from '@kbn/guided-onboarding/src/components/landing_page/guide_filters';
import { ReactWrapper } from '@kbn/test-jest-helpers/src/testbed/types';
const mockCloud = cloudMock.createSetup();
const mockChrome = chromeServiceMock.createStartContract();
@ -48,9 +51,13 @@ describe('getting started', () => {
});
test('should render getting started component', async () => {
const component = await shallow(<GettingStarted />);
const component = await shallow(
<MemoryRouter>
<GettingStarted />
</MemoryRouter>
);
expect(component).toMatchSnapshot();
expect(component.find('GettingStarted').exists()).toBe(true);
});
test('displays loading indicator', async () => {
@ -99,4 +106,37 @@ describe('getting started', () => {
expect(localStorage.getItem(KEY_ENABLE_WELCOME)).toBe('false');
});
test('should set default guide filter value if querystring parameter does NOT exist', async () => {
let component: ReactWrapper;
await act(async () => {
component = mountWithIntl(
<MemoryRouter>
<GettingStarted />
</MemoryRouter>
);
});
const guideFilters = component!.find('[data-test-subj="onboarding--guideFilters"]');
expect((guideFilters.props() as GuideFiltersProps).activeFilter).toBe('all');
});
test('should auto-select guide filter value based on querystring parameter', async () => {
const cloudDiscoveryUseCase = 'observability';
let component: ReactWrapper;
await act(async () => {
component = mountWithIntl(
<MemoryRouter
initialEntries={[{ pathname: '/', search: `?useCase=${cloudDiscoveryUseCase}` }]}
>
<GettingStarted />
</MemoryRouter>
);
});
const guideFilters = component!.find('[data-test-subj="onboarding--guideFilters"]');
expect((guideFilters.props() as GuideFiltersProps).activeFilter).toBe(cloudDiscoveryUseCase);
});
});

View file

@ -7,6 +7,7 @@
*/
import React, { useCallback, useEffect, useState } from 'react';
import { parse } from 'query-string';
import {
EuiButton,
EuiLink,
@ -19,7 +20,7 @@ import {
} from '@elastic/eui';
import { css } from '@emotion/react';
import { useHistory } from 'react-router-dom';
import { useHistory, useLocation } from 'react-router-dom';
import { METRIC_TYPE } from '@kbn/analytics';
import { i18n } from '@kbn/i18n';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
@ -48,7 +49,26 @@ export const GettingStarted = () => {
const [guidesState, setGuidesState] = useState<GuideState[]>([]);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [filter, setFilter] = useState<GuideFilterValues>('all');
const { search } = useLocation();
const query = parse(search);
const isTypeOfGuideFilterValue = (useCase: string | string[] | null) => {
const filterValues: string[] = ['search', 'observability', 'security', 'all']; // list of GuideFilterValues types
if (!useCase) {
return false;
}
if (useCase instanceof Array) {
return filterValues.includes(useCase[0]);
}
return filterValues.includes(useCase);
};
const [filter, setFilter] = useState<GuideFilterValues>(
isTypeOfGuideFilterValue(query.useCase) ? (query.useCase as GuideFilterValues) : 'all'
);
const history = useHistory();
useEffect(() => {
@ -196,7 +216,12 @@ export const GettingStarted = () => {
</EuiText>
<EuiSpacer size="s" />
<EuiSpacer size="xxl" />
<GuideFilters activeFilter={filter} setActiveFilter={setFilter} />
<GuideFilters
application={application}
activeFilter={filter}
setActiveFilter={setFilter}
data-test-subj="onboarding--guideFilters"
/>
<EuiSpacer size="xxl" />
<GuideCards
activateGuide={activateGuide}