[Guided onboarding] Registers 3 separate guide IDs for search (#149968)

## Summary
Fixes https://github.com/elastic/kibana/issues/149691 
Follow up to https://github.com/elastic/kibana/pull/149528 

This PR adds 3 separate guide ID values `appSearch`, `websiteSearch` and
`databaseSearch` instead of previously used `search`. With that change,
each card for Search on the landing page has its own independent guide
that can be completed.

Example plugin for guided onboarding is also updated so that all 3
search guides can be managed there. Otherwise there should not be any
UI/UX changes.

Currently, all 3 ids are registered with the same config and that can be
easily updated by the team in the future.

#### Screenshot
<img width="485" alt="Screenshot 2023-01-31 at 18 24 12"
src="https://user-images.githubusercontent.com/6585477/215840343-f9a65be3-0568-40ca-b458-5de92bfe8aaf.png">


### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Yulia Čech 2023-02-06 14:16:26 +01:00 committed by GitHub
parent af3ae7b555
commit 8705a6a77b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 76 additions and 26 deletions

View file

@ -25,6 +25,7 @@ import {
EuiText,
EuiTitle,
EuiSelectOption,
EuiFlexGrid,
} from '@elastic/eui';
import type { GuideState, GuideStepIds, GuideId, GuideStep } from '@kbn/guided-onboarding';
import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public';
@ -34,7 +35,14 @@ interface MainProps {
notifications: CoreStart['notifications'];
}
const exampleGuideIds: GuideId[] = ['search', 'siem', 'kubernetes', 'testGuide'];
const exampleGuideIds: GuideId[] = [
'appSearch',
'websiteSearch',
'databaseSearch',
'siem',
'kubernetes',
'testGuide',
];
const selectOptions: EuiSelectOption[] = exampleGuideIds.map((guideId) => ({
value: guideId,
text: guideId,
@ -211,7 +219,7 @@ export const Main = (props: MainProps) => {
</h3>
</EuiText>
<EuiSpacer />
<EuiFlexGroup>
<EuiFlexGrid columns={3}>
{exampleGuideIds.map((guideId) => {
const guideState = guidesState?.find((guide) => guide.guideId === guideId);
return (
@ -255,7 +263,7 @@ export const Main = (props: MainProps) => {
</EuiFlexItem>
);
})}
</EuiFlexGroup>
</EuiFlexGrid>
<EuiSpacer />
<EuiHorizontalRule />
<EuiText>

View file

@ -15,7 +15,7 @@ exports[`guide cards snapshots should render all cards 1`] = `
activeFilter="all"
card={
Object {
"guideId": "search",
"guideId": "appSearch",
"order": 1,
"solution": "search",
"telemetryId": "onboarding--search--application",
@ -87,7 +87,7 @@ exports[`guide cards snapshots should render all cards 1`] = `
activeFilter="all"
card={
Object {
"guideId": "search",
"guideId": "websiteSearch",
"order": 4,
"solution": "search",
"telemetryId": "onboarding--search--website",
@ -162,7 +162,7 @@ exports[`guide cards snapshots should render all cards 1`] = `
activeFilter="all"
card={
Object {
"guideId": "search",
"guideId": "databaseSearch",
"order": 7,
"solution": "search",
"telemetryId": "onboarding--search--database",

View file

@ -33,7 +33,7 @@ export const guideCards: GuideCardConstants[] = [
title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.appSearch.title', {
defaultMessage: 'Build an application on top of Elasticsearch',
}),
guideId: 'search',
guideId: 'appSearch',
telemetryId: 'onboarding--search--application',
order: 1,
},
@ -42,7 +42,7 @@ export const guideCards: GuideCardConstants[] = [
title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.websiteSearch.title', {
defaultMessage: 'Add search to my website',
}),
guideId: 'search',
guideId: 'websiteSearch',
telemetryId: 'onboarding--search--website',
order: 4,
},
@ -51,7 +51,7 @@ export const guideCards: GuideCardConstants[] = [
title: i18n.translate('guidedOnboardingPackage.gettingStarted.cards.databaseSearch.title', {
defaultMessage: 'Search across databases and business systems',
}),
guideId: 'search',
guideId: 'databaseSearch',
telemetryId: 'onboarding--search--database',
order: 7,
},

View file

@ -6,7 +6,13 @@
* Side Public License, v 1.
*/
export type GuideId = 'kubernetes' | 'siem' | 'search' | 'testGuide';
export type GuideId =
| 'kubernetes'
| 'siem'
| 'appSearch'
| 'websiteSearch'
| 'databaseSearch'
| 'testGuide';
type KubernetesStepIds = 'add_data' | 'view_dashboard' | 'tour_observability';
type SiemStepIds = 'add_data' | 'rules' | 'alertsCases';

View file

@ -15,7 +15,7 @@ export default function testGetGuideConfig({ getService }: FtrProviderContext) {
describe('GET /api/guided_onboarding/configs', () => {
// check that production guides are present
['siem', 'search', 'kubernetes'].map((guideId) => {
['siem', 'appSearch', 'websiteSearch', 'databaseSearch', 'kubernetes'].map((guideId) => {
it(`returns config for ${guideId}`, async () => {
const response = await supertest.get(`${getConfigsPath}/${guideId}`).expect(200);
expect(response.body).not.to.be.empty();

View file

@ -12,6 +12,7 @@ import {
guideStateSavedObjectsType,
pluginStateSavedObjectsType,
} from '@kbn/guided-onboarding-plugin/server/saved_objects/guided_setup';
import { appSearchGuideId } from '@kbn/enterprise-search-plugin/common/guided_onboarding/search_guide_config';
import type { FtrProviderContext } from '../../ftr_provider_context';
import { createGuides } from './helpers';
@ -37,13 +38,13 @@ export default function testGetGuidesState({ getService }: FtrProviderContext) {
it('returns all created guides (active and inactive)', async () => {
await createGuides(kibanaServer, [
testGuideStep1ActiveState,
{ ...testGuideStep1ActiveState, guideId: 'search' },
{ ...testGuideStep1ActiveState, guideId: appSearchGuideId },
]);
const response = await supertest.get(getGuidesPath).expect(200);
expect(response.body).not.to.be.empty();
expect(response.body.state).to.eql([
testGuideStep1ActiveState,
{ ...testGuideStep1ActiveState, guideId: 'search' },
{ ...testGuideStep1ActiveState, guideId: appSearchGuideId },
]);
});
});

View file

@ -17,6 +17,7 @@ import {
guideStateSavedObjectsType,
} from '@kbn/guided-onboarding-plugin/server/saved_objects/guided_setup';
import { testGuideId } from '@kbn/guided-onboarding';
import { appSearchGuideId } from '@kbn/enterprise-search-plugin/common/guided_onboarding/search_guide_config';
import type { FtrProviderContext } from '../../ftr_provider_context';
import { createGuides, createPluginState } from './helpers';
@ -126,7 +127,7 @@ export default function testPutState({ getService }: FtrProviderContext) {
// create an active guide and an inactive guide
await createGuides(kibanaServer, [
testGuideStep1ActiveState,
{ ...testGuideNotActiveState, guideId: 'search' },
{ ...testGuideNotActiveState, guideId: appSearchGuideId },
]);
// Create a new guide with isActive: true
@ -150,7 +151,7 @@ export default function testPutState({ getService }: FtrProviderContext) {
const searchGuideSO = await kibanaServer.savedObjects.get({
type: guideStateSavedObjectsType,
id: 'search',
id: appSearchGuideId,
});
expect(searchGuideSO.attributes.isActive).to.eql(false);

View file

@ -67,5 +67,6 @@
"@kbn/safer-lodash-set",
"@kbn/utility-types",
"@kbn/dev-proc-runner",
"@kbn/enterprise-search-plugin",
]
}

View file

@ -8,8 +8,10 @@
import type { GuideConfig } from '@kbn/guided-onboarding';
import { i18n } from '@kbn/i18n';
export const searchGuideId = 'search';
export const searchGuideConfig: GuideConfig = {
export const appSearchGuideId = 'appSearch';
export const websiteSearchGuideId = 'websiteSearch';
export const databaseSearchGuideId = 'databaseSearch';
const guideConfig: Omit<GuideConfig, 'telemetryId'> = {
title: i18n.translate('xpack.enterpriseSearch.guideConfig.title', {
defaultMessage: 'Search my data',
}),
@ -18,7 +20,6 @@ export const searchGuideConfig: GuideConfig = {
'Build custom search experiences with your data using Elastics out-of-the-box web crawler, connectors, and robust APIs. Gain deep insights from the built-in search analytics to curate results and optimize relevance.',
}),
guideName: 'Enterprise Search',
telemetryId: 'search',
steps: [
{
id: 'add_data',
@ -90,3 +91,15 @@ export const searchGuideConfig: GuideConfig = {
},
],
};
export const appSearchGuideConfig: GuideConfig = {
...guideConfig,
telemetryId: 'appSearch',
};
export const websiteSearchGuideConfig: GuideConfig = {
...guideConfig,
telemetryId: 'websiteSearch',
};
export const databaseSearchGuideConfig: GuideConfig = {
...guideConfig,
telemetryId: 'databaseSearch',
};

View file

@ -68,18 +68,29 @@ export const SearchIndex: React.FC = () => {
const { indexName } = useValues(IndexNameLogic);
/**
* Guided Onboarding needs us to mark the add data step as complete as soon as the user has data in an index
* Guided Onboarding needs us to mark the add data step as complete as soon as the user has data in an index.
* This needs to be checked for any of the 3 registered search guideIds
* Putting it here guarantees that if a user is viewing an index with data, it'll be marked as complete
*/
const { guidedOnboarding } = useValues(KibanaLogic);
const isDataStepActive = useObservable(
guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('search', 'add_data')
const isAppGuideActive = useObservable(
guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('appSearch', 'add_data')
);
const isWebsiteGuideActive = useObservable(
guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('websiteSearch', 'add_data')
);
const isDatabaseGuideActive = useObservable(
guidedOnboarding.guidedOnboardingApi!.isGuideStepActive$('databaseSearch', 'add_data')
);
useEffect(() => {
if (isDataStepActive && index?.count) {
guidedOnboarding.guidedOnboardingApi?.completeGuideStep('search', 'add_data');
if (isAppGuideActive && index?.count) {
guidedOnboarding.guidedOnboardingApi?.completeGuideStep('appSearch', 'add_data');
} else if (isWebsiteGuideActive && index?.count) {
guidedOnboarding.guidedOnboardingApi?.completeGuideStep('websiteSearch', 'add_data');
} else if (isDatabaseGuideActive && index?.count) {
guidedOnboarding.guidedOnboardingApi?.completeGuideStep('databaseSearch', 'add_data');
}
}, [isDataStepActive, index?.count]);
}, [isAppGuideActive, isWebsiteGuideActive, isDatabaseGuideActive, index?.count]);
useEffect(() => {
if (

View file

@ -38,7 +38,14 @@ import {
ENTERPRISE_SEARCH_ANALYTICS_LOGS_SOURCE_ID,
} from '../common/constants';
import { searchGuideId, searchGuideConfig } from '../common/guided_onboarding/search_guide_config';
import {
appSearchGuideId,
websiteSearchGuideId,
databaseSearchGuideId,
appSearchGuideConfig,
websiteSearchGuideConfig,
databaseSearchGuideConfig,
} from '../common/guided_onboarding/search_guide_config';
import { registerTelemetryUsageCollector as registerASTelemetryUsageCollector } from './collectors/app_search/telemetry';
import { registerTelemetryUsageCollector as registerESTelemetryUsageCollector } from './collectors/enterprise_search/telemetry';
@ -264,7 +271,9 @@ export class EnterpriseSearchPlugin implements Plugin {
/**
* Register a config for the search guide
*/
guidedOnboarding.registerGuideConfig(searchGuideId, searchGuideConfig);
guidedOnboarding.registerGuideConfig(appSearchGuideId, appSearchGuideConfig);
guidedOnboarding.registerGuideConfig(websiteSearchGuideId, websiteSearchGuideConfig);
guidedOnboarding.registerGuideConfig(databaseSearchGuideId, databaseSearchGuideConfig);
}
public start() {}