mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[Guided onboarding] Added api functions for the active step (#141045)
* [Guided onboarding] Initial service function to check if the step is active and to complete a step * [Guided onboarding] Added unit tests for api and helpers * [Guided onboarding] Added jsdoc comments to the api methods
This commit is contained in:
parent
f2bb8974f7
commit
081f53a220
11 changed files with 323 additions and 35 deletions
|
@ -10,9 +10,10 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { FormattedMessage } from '@kbn/i18n-react';
|
import { FormattedMessage } from '@kbn/i18n-react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { CoreStart } from '@kbn/core/public';
|
||||||
import {
|
import {
|
||||||
EuiButton,
|
EuiButton,
|
||||||
EuiFieldNumber,
|
EuiFieldText,
|
||||||
EuiFlexGroup,
|
EuiFlexGroup,
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
EuiFormRow,
|
EuiFormRow,
|
||||||
|
@ -29,7 +30,6 @@ import {
|
||||||
GuidedOnboardingState,
|
GuidedOnboardingState,
|
||||||
UseCase,
|
UseCase,
|
||||||
} from '@kbn/guided-onboarding-plugin/public';
|
} from '@kbn/guided-onboarding-plugin/public';
|
||||||
import { CoreStart } from '@kbn/core/public';
|
|
||||||
|
|
||||||
interface MainProps {
|
interface MainProps {
|
||||||
guidedOnboarding: GuidedOnboardingPluginStart;
|
guidedOnboarding: GuidedOnboardingPluginStart;
|
||||||
|
@ -51,7 +51,9 @@ export const Main = (props: MainProps) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = guidedOnboardingApi?.fetchGuideState$().subscribe((newState) => {
|
const subscription = guidedOnboardingApi
|
||||||
|
?.fetchGuideState$()
|
||||||
|
.subscribe((newState: GuidedOnboardingState) => {
|
||||||
setGuideState(newState);
|
setGuideState(newState);
|
||||||
});
|
});
|
||||||
return () => subscription?.unsubscribe();
|
return () => subscription?.unsubscribe();
|
||||||
|
@ -208,7 +210,7 @@ export const Main = (props: MainProps) => {
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem>
|
<EuiFlexItem>
|
||||||
<EuiFormRow label="Step">
|
<EuiFormRow label="Step">
|
||||||
<EuiFieldNumber
|
<EuiFieldText
|
||||||
value={selectedStep}
|
value={selectedStep}
|
||||||
onChange={(e) => setSelectedStep(e.target.value)}
|
onChange={(e) => setSelectedStep(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -18,6 +18,8 @@ import {
|
||||||
EuiSpacer,
|
EuiSpacer,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
|
import useObservable from 'react-use/lib/useObservable';
|
||||||
|
|
||||||
import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public/types';
|
import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public/types';
|
||||||
|
|
||||||
interface GuidedOnboardingExampleAppDeps {
|
interface GuidedOnboardingExampleAppDeps {
|
||||||
|
@ -28,17 +30,14 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) =>
|
||||||
const { guidedOnboardingApi } = guidedOnboarding;
|
const { guidedOnboardingApi } = guidedOnboarding;
|
||||||
|
|
||||||
const [isTourStepOpen, setIsTourStepOpen] = useState<boolean>(false);
|
const [isTourStepOpen, setIsTourStepOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const isTourActive = useObservable(
|
||||||
|
guidedOnboardingApi!.isGuideStepActive$('search', 'add_data'),
|
||||||
|
false
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = guidedOnboardingApi?.fetchGuideState$().subscribe((newState) => {
|
setIsTourStepOpen(isTourActive);
|
||||||
const { activeGuide: guide, activeStep: step } = newState;
|
}, [isTourActive]);
|
||||||
|
|
||||||
if (guide === 'search' && step === 'add_data') {
|
|
||||||
setIsTourStepOpen(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return () => subscription?.unsubscribe();
|
|
||||||
}, [guidedOnboardingApi]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EuiPageContentHeader>
|
<EuiPageContentHeader>
|
||||||
|
@ -79,10 +78,7 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) =>
|
||||||
>
|
>
|
||||||
<EuiButton
|
<EuiButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await guidedOnboardingApi?.updateGuideState({
|
await guidedOnboardingApi?.completeGuideStep('search', 'add_data');
|
||||||
activeGuide: 'search',
|
|
||||||
activeStep: 'search_experience',
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Complete step 1
|
Complete step 1
|
||||||
|
|
|
@ -80,10 +80,7 @@ export const StepTwo = (props: StepTwoProps) => {
|
||||||
>
|
>
|
||||||
<EuiButton
|
<EuiButton
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await guidedOnboardingApi?.updateGuideState({
|
await guidedOnboardingApi?.completeGuideStep('search', 'search_experience');
|
||||||
activeGuide: 'search',
|
|
||||||
activeStep: 'optimize',
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Complete step 2
|
Complete step 2
|
||||||
|
|
19
src/plugins/guided_onboarding/jest.config.js
Normal file
19
src/plugins/guided_onboarding/jest.config.js
Normal 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
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
preset: '@kbn/test',
|
||||||
|
rootDir: '../../..',
|
||||||
|
roots: ['<rootDir>/src/plugins/guided_onboarding'],
|
||||||
|
testRunner: 'jasmine2',
|
||||||
|
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/src/plugins/guided_onboarding',
|
||||||
|
coverageReporters: ['text', 'html'],
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'<rootDir>/src/plugins/guided_onboarding/{common,public,server}/**/*.{ts,tsx}',
|
||||||
|
],
|
||||||
|
};
|
|
@ -31,7 +31,7 @@ import {
|
||||||
import { ApplicationStart } from '@kbn/core-application-browser';
|
import { ApplicationStart } from '@kbn/core-application-browser';
|
||||||
import { HttpStart } from '@kbn/core-http-browser';
|
import { HttpStart } from '@kbn/core-http-browser';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { guidesConfig } from '../constants';
|
import { guidesConfig } from '../constants/guides_config';
|
||||||
import type { GuideConfig, StepStatus, GuidedOnboardingState, StepConfig } from '../types';
|
import type { GuideConfig, StepStatus, GuidedOnboardingState, StepConfig } from '../types';
|
||||||
import type { ApiService } from '../services/api';
|
import type { ApiService } from '../services/api';
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,10 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { GuidesConfig } from '../types';
|
||||||
import { securityConfig } from './security';
|
import { securityConfig } from './security';
|
||||||
import { observabilityConfig } from './observability';
|
import { observabilityConfig } from './observability';
|
||||||
import { searchConfig } from './search';
|
import { searchConfig } from './search';
|
||||||
import type { GuideConfig, UseCase } from '../types';
|
|
||||||
|
|
||||||
type GuidesConfig = {
|
|
||||||
[key in UseCase]: GuideConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const guidesConfig: GuidesConfig = {
|
export const guidesConfig: GuidesConfig = {
|
||||||
security: securityConfig,
|
security: securityConfig,
|
133
src/plugins/guided_onboarding/public/services/api.test.ts
Normal file
133
src/plugins/guided_onboarding/public/services/api.test.ts
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { HttpSetup } from '@kbn/core/public';
|
||||||
|
import { httpServiceMock } from '@kbn/core/public/mocks';
|
||||||
|
import { firstValueFrom, Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { API_BASE_PATH } from '../../common';
|
||||||
|
import { ApiService } from './api';
|
||||||
|
import { GuidedOnboardingState } from '..';
|
||||||
|
|
||||||
|
const searchGuide = 'search';
|
||||||
|
const firstStep = 'add_data';
|
||||||
|
const secondStep = 'search_experience';
|
||||||
|
const lastStep = 'review';
|
||||||
|
|
||||||
|
describe('GuidedOnboarding ApiService', () => {
|
||||||
|
let httpClient: jest.Mocked<HttpSetup>;
|
||||||
|
let apiService: ApiService;
|
||||||
|
let subscription: Subscription;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
httpClient = httpServiceMock.createStartContract({ basePath: '/base/path' });
|
||||||
|
httpClient.get.mockResolvedValue({
|
||||||
|
state: { activeGuide: searchGuide, activeStep: firstStep },
|
||||||
|
});
|
||||||
|
apiService = new ApiService();
|
||||||
|
apiService.setup(httpClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (subscription) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fetchGuideState$', () => {
|
||||||
|
it('sends a request to the get API', () => {
|
||||||
|
subscription = apiService.fetchGuideState$().subscribe();
|
||||||
|
expect(httpClient.get).toHaveBeenCalledTimes(1);
|
||||||
|
expect(httpClient.get).toHaveBeenCalledWith(`${API_BASE_PATH}/state`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('broadcasts the updated state', async () => {
|
||||||
|
await apiService.updateGuideState({
|
||||||
|
activeGuide: searchGuide,
|
||||||
|
activeStep: secondStep,
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = await firstValueFrom(apiService.fetchGuideState$());
|
||||||
|
expect(state).toEqual({ activeGuide: searchGuide, activeStep: secondStep });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateGuideState', () => {
|
||||||
|
it('sends a request to the put API', async () => {
|
||||||
|
const state = {
|
||||||
|
activeGuide: searchGuide,
|
||||||
|
activeStep: secondStep,
|
||||||
|
};
|
||||||
|
await apiService.updateGuideState(state as GuidedOnboardingState);
|
||||||
|
expect(httpClient.put).toHaveBeenCalledTimes(1);
|
||||||
|
expect(httpClient.put).toHaveBeenCalledWith(`${API_BASE_PATH}/state`, {
|
||||||
|
body: JSON.stringify(state),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isGuideStepActive$', () => {
|
||||||
|
it('returns true if the step is active', async (done) => {
|
||||||
|
subscription = apiService
|
||||||
|
.isGuideStepActive$(searchGuide, firstStep)
|
||||||
|
.subscribe((isStepActive) => {
|
||||||
|
if (isStepActive) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if the step is not active', async (done) => {
|
||||||
|
subscription = apiService
|
||||||
|
.isGuideStepActive$(searchGuide, secondStep)
|
||||||
|
.subscribe((isStepActive) => {
|
||||||
|
if (!isStepActive) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('completeGuideStep', () => {
|
||||||
|
it(`completes the step when it's active`, async () => {
|
||||||
|
await apiService.completeGuideStep(searchGuide, firstStep);
|
||||||
|
expect(httpClient.put).toHaveBeenCalledTimes(1);
|
||||||
|
// this assertion depends on the guides config, we are checking for the next step
|
||||||
|
expect(httpClient.put).toHaveBeenCalledWith(`${API_BASE_PATH}/state`, {
|
||||||
|
body: JSON.stringify({
|
||||||
|
activeGuide: searchGuide,
|
||||||
|
activeStep: secondStep,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`completes the guide when the last step is active`, async () => {
|
||||||
|
httpClient.get.mockResolvedValue({
|
||||||
|
// this state depends on the guides config
|
||||||
|
state: { activeGuide: searchGuide, activeStep: lastStep },
|
||||||
|
});
|
||||||
|
apiService.setup(httpClient);
|
||||||
|
|
||||||
|
await apiService.completeGuideStep(searchGuide, lastStep);
|
||||||
|
expect(httpClient.put).toHaveBeenCalledTimes(1);
|
||||||
|
// this assertion depends on the guides config, we are checking for the last step
|
||||||
|
expect(httpClient.put).toHaveBeenCalledWith(`${API_BASE_PATH}/state`, {
|
||||||
|
body: JSON.stringify({
|
||||||
|
activeGuide: searchGuide,
|
||||||
|
activeStep: 'completed',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`does nothing if the step is not active`, async () => {
|
||||||
|
await apiService.completeGuideStep(searchGuide, secondStep);
|
||||||
|
expect(httpClient.put).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,10 +7,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { HttpSetup } from '@kbn/core/public';
|
import { HttpSetup } from '@kbn/core/public';
|
||||||
import { BehaviorSubject, map, from, concatMap, of } from 'rxjs';
|
import { BehaviorSubject, map, from, concatMap, of, Observable, firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
import { API_BASE_PATH } from '../../common';
|
import { API_BASE_PATH } from '../../common';
|
||||||
import { GuidedOnboardingState } from '../types';
|
import { GuidedOnboardingState, UseCase } from '../types';
|
||||||
|
import { getNextStep, isLastStep } from './helpers';
|
||||||
|
|
||||||
export class ApiService {
|
export class ApiService {
|
||||||
private client: HttpSetup | undefined;
|
private client: HttpSetup | undefined;
|
||||||
|
@ -21,7 +22,12 @@ export class ApiService {
|
||||||
this.onboardingGuideState$ = new BehaviorSubject<GuidedOnboardingState | undefined>(undefined);
|
this.onboardingGuideState$ = new BehaviorSubject<GuidedOnboardingState | undefined>(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchGuideState$() {
|
/**
|
||||||
|
* An Observable with the guided onboarding state.
|
||||||
|
* Initially the state is fetched from the backend.
|
||||||
|
* Subsequently, the observable is updated automatically, when the state changes.
|
||||||
|
*/
|
||||||
|
public fetchGuideState$(): Observable<GuidedOnboardingState> {
|
||||||
// TODO add error handling if this.client has not been initialized or request fails
|
// TODO add error handling if this.client has not been initialized or request fails
|
||||||
return this.onboardingGuideState$.pipe(
|
return this.onboardingGuideState$.pipe(
|
||||||
concatMap((state) =>
|
concatMap((state) =>
|
||||||
|
@ -34,7 +40,14 @@ export class ApiService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateGuideState(newState: GuidedOnboardingState) {
|
/**
|
||||||
|
* Updates the state of the guided onboarding
|
||||||
|
* @param {GuidedOnboardingState} newState the new state of the guided onboarding
|
||||||
|
* @return {Promise} a promise with the updated state or undefined if the update fails
|
||||||
|
*/
|
||||||
|
public async updateGuideState(
|
||||||
|
newState: GuidedOnboardingState
|
||||||
|
): Promise<{ state: GuidedOnboardingState } | undefined> {
|
||||||
if (!this.client) {
|
if (!this.client) {
|
||||||
throw new Error('ApiService has not be initialized.');
|
throw new Error('ApiService has not be initialized.');
|
||||||
}
|
}
|
||||||
|
@ -54,6 +67,51 @@ export class ApiService {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An observable with the boolean value if the step is active.
|
||||||
|
* Returns true, if the passed params identify the guide step that is currently active.
|
||||||
|
* Returns false otherwise.
|
||||||
|
* @param {string} guideID the id of the guide (one of search, observability, security)
|
||||||
|
* @param {string} stepID the id of the step in the guide
|
||||||
|
* @return {Observable} an observable with the boolean value
|
||||||
|
*/
|
||||||
|
public isGuideStepActive$(guideID: string, stepID: string): Observable<boolean> {
|
||||||
|
return this.fetchGuideState$().pipe(
|
||||||
|
map((state) => {
|
||||||
|
return state ? state.activeGuide === guideID && state.activeStep === stepID : false;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes the guide step identified by the passed params.
|
||||||
|
* A noop if the passed step is not active.
|
||||||
|
* Completes the current guide, if the step is the last one in the guide.
|
||||||
|
* @param {string} guideID the id of the guide (one of search, observability, security)
|
||||||
|
* @param {string} stepID the id of the step in the guide
|
||||||
|
* @return {Promise} a promise with the updated state or undefined if the operation fails
|
||||||
|
*/
|
||||||
|
public async completeGuideStep(
|
||||||
|
guideID: string,
|
||||||
|
stepID: string
|
||||||
|
): Promise<{ state: GuidedOnboardingState } | undefined> {
|
||||||
|
const isStepActive = await firstValueFrom(this.isGuideStepActive$(guideID, stepID));
|
||||||
|
if (isStepActive) {
|
||||||
|
if (isLastStep(guideID, stepID)) {
|
||||||
|
await this.updateGuideState({ activeGuide: guideID as UseCase, activeStep: 'completed' });
|
||||||
|
} else {
|
||||||
|
const nextStepID = getNextStep(guideID, stepID);
|
||||||
|
if (nextStepID !== undefined) {
|
||||||
|
await this.updateGuideState({
|
||||||
|
activeGuide: guideID as UseCase,
|
||||||
|
activeStep: nextStepID,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const apiService = new ApiService();
|
export const apiService = new ApiService();
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getNextStep, isLastStep } from './helpers';
|
||||||
|
|
||||||
|
describe('GuidedOnboarding ApiService helpers', () => {
|
||||||
|
// this test suite depends on the guides config
|
||||||
|
describe('isLastStepActive', () => {
|
||||||
|
it('returns true if the passed params are for the last step', () => {
|
||||||
|
const result = isLastStep('search', 'review');
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false if the passed params are not for the last step', () => {
|
||||||
|
const result = isLastStep('search', 'add_data');
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getNextStep', () => {
|
||||||
|
it('returns id of the next step', () => {
|
||||||
|
const result = getNextStep('search', 'add_data');
|
||||||
|
expect(result).toEqual('search_experience');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined if the params are not part of the config', () => {
|
||||||
|
const result = getNextStep('some_guide', 'some_step');
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`returns undefined if it's the last step`, () => {
|
||||||
|
const result = getNextStep('search', 'review');
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
42
src/plugins/guided_onboarding/public/services/helpers.ts
Normal file
42
src/plugins/guided_onboarding/public/services/helpers.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { guidesConfig } from '../constants/guides_config';
|
||||||
|
import { GuideConfig, StepConfig, UseCase } from '../types';
|
||||||
|
|
||||||
|
const getGuideConfig = (guideID?: string): GuideConfig | undefined => {
|
||||||
|
if (guideID && Object.keys(guidesConfig).includes(guideID)) {
|
||||||
|
return guidesConfig[guideID as UseCase];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStepIndex = (guideID: string, stepID: string): number => {
|
||||||
|
const guide = getGuideConfig(guideID);
|
||||||
|
if (guide) {
|
||||||
|
return guide.steps.findIndex((step: StepConfig) => step.id === stepID);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isLastStep = (guideID: string, stepID: string): boolean => {
|
||||||
|
const guide = getGuideConfig(guideID);
|
||||||
|
const activeStepIndex = getStepIndex(guideID, stepID);
|
||||||
|
const stepsNumber = guide?.steps.length || 0;
|
||||||
|
if (stepsNumber > 0) {
|
||||||
|
return activeStepIndex === stepsNumber - 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getNextStep = (guideID: string, stepID: string): string | undefined => {
|
||||||
|
const guide = getGuideConfig(guideID);
|
||||||
|
const activeStepIndex = getStepIndex(guideID, stepID);
|
||||||
|
if (activeStepIndex > -1 && guide?.steps[activeStepIndex + 1]) {
|
||||||
|
return guide?.steps[activeStepIndex + 1].id;
|
||||||
|
}
|
||||||
|
};
|
|
@ -44,9 +44,13 @@ export interface GuideConfig {
|
||||||
steps: StepConfig[];
|
steps: StepConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GuidesConfig = {
|
||||||
|
[key in UseCase]: GuideConfig;
|
||||||
|
};
|
||||||
|
|
||||||
export interface GuidedOnboardingState {
|
export interface GuidedOnboardingState {
|
||||||
activeGuide: UseCase | 'unset';
|
activeGuide: UseCase | 'unset';
|
||||||
activeStep: string | 'unset';
|
activeStep: string | 'unset' | 'completed';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClientConfigType {
|
export interface ClientConfigType {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue