[Guided onboarding] Dynamic URLs for steps (#154572)

## Summary
Fixes https://github.com/elastic/kibana/issues/143322

This PR adds an option to store dynamic parameters when a step is
completed to be used later for dynamically built URLs.

### How to test
1. Add `xpack.cloud.id: 'testID'` to your `/config/kibana.dev.yml` file
2. Start ES with `yarn es snapshot`
3. Start Kibana with `yarn start --run-examples` 
4. Navigate to the guided onboarding example plugin
`http://localhost:5601/app/guidedOnboardingExample`
5. Start the test guide and when completing step 1 provide any value for
the paramter `indexName`
6. Continue the guide until step 4 and check that the correct value is
being used for the url of this step.

### 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: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Yulia Čech 2023-04-21 18:21:29 +02:00 committed by GitHub
parent 89258d7a1c
commit bc3471cf26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 403 additions and 39 deletions

View file

@ -25,6 +25,7 @@ import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/publi
import { StepTwo } from './step_two';
import { StepOne } from './step_one';
import { StepThree } from './step_three';
import { StepFour } from './step_four';
import { Main } from './main';
interface GuidedOnboardingExampleAppDeps {
@ -65,6 +66,13 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps
<Route exact path="/stepThree">
<StepThree guidedOnboarding={guidedOnboarding} />
</Route>
p
<Route
path="/stepFour/:indexName?"
render={(routeProps) => (
<StepFour guidedOnboarding={guidedOnboarding} {...routeProps} />
)}
/>
</Switch>
</Router>
</EuiPageContent>

View file

@ -345,6 +345,14 @@ export const Main = (props: MainProps) => {
/>
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={() => history.push('stepFour')}>
<FormattedMessage
id="guidedOnboardingExample.main.examplePages.stepFour.link"
defaultMessage="Step 4"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentBody>
</>

View file

@ -0,0 +1,83 @@
/*
* 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 React, { useEffect, useState } from 'react';
import { EuiButton, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public/types';
import { FormattedMessage } from '@kbn/i18n-react';
import {
EuiPageContentHeader_Deprecated as EuiPageContentHeader,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiCode,
} from '@elastic/eui';
import { RouteComponentProps } from 'react-router-dom';
interface StepFourProps {
guidedOnboarding: GuidedOnboardingPluginStart;
}
export const StepFour = (props: StepFourProps & RouteComponentProps<{ indexName: string }>) => {
const {
guidedOnboarding: { guidedOnboardingApi },
match: {
params: { indexName },
},
} = props;
const [, setIsTourStepOpen] = useState<boolean>(false);
useEffect(() => {
const subscription = guidedOnboardingApi
?.isGuideStepActive$('testGuide', 'step4')
.subscribe((isStepActive) => {
setIsTourStepOpen(isStepActive);
});
return () => subscription?.unsubscribe();
}, [guidedOnboardingApi]);
return (
<>
<EuiPageContentHeader>
<EuiTitle>
<h2>
<FormattedMessage
id="guidedOnboardingExample.stepFour.title"
defaultMessage="Example step 4"
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiText>
<p>
<FormattedMessage
id="guidedOnboardingExample.guidesSelection.stepFour.explanation"
defaultMessage="This step has a dynamic URL with a param {indexName} passed in step 1"
values={{
indexName: (
<EuiCode language="javascript">&#123;indexName: {indexName}&#125;</EuiCode>
),
}}
/>
</p>
</EuiText>
<EuiSpacer />
<EuiButton
onClick={async () => {
await guidedOnboardingApi?.completeGuideStep('testGuide', 'step4');
}}
>
Complete step 4
</EuiButton>
</EuiPageContentBody>
</>
);
};

View file

@ -16,6 +16,11 @@ import {
EuiPageContentHeader_Deprecated as EuiPageContentHeader,
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiSpacer,
EuiCode,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
} from '@elastic/eui';
import useObservable from 'react-use/lib/useObservable';
@ -30,6 +35,7 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) =>
const { guidedOnboardingApi } = guidedOnboarding;
const [isTourStepOpen, setIsTourStepOpen] = useState<boolean>(false);
const [indexName, setIndexName] = useState('test1234');
const isTourActive = useObservable(
guidedOnboardingApi!.isGuideStepActive$('testGuide', 'step1'),
@ -59,30 +65,59 @@ export const StepOne = ({ guidedOnboarding }: GuidedOnboardingExampleAppDeps) =>
Test guide, step 1, a EUI tour will be displayed, pointing to the button below."
/>
</p>
<p>
<FormattedMessage
id="guidedOnboardingExample.guidesSelection.stepOne.dynamicParamsExplanation"
defaultMessage="There is also an input field to provide a dynamic parameter {indexName} for step 4."
values={{
indexName: <EuiCode language="javascript">indexName</EuiCode>,
}}
/>
</p>
</EuiText>
<EuiSpacer />
<EuiTourStep
content={
<EuiText>
<p>Click this button to complete step 1.</p>
</EuiText>
}
isStepOpen={isTourStepOpen}
minWidth={300}
onFinish={() => setIsTourStepOpen(false)}
step={1}
stepsTotal={1}
title="Step 1"
anchorPosition="rightUp"
>
<EuiButton
onClick={async () => {
await guidedOnboardingApi?.completeGuideStep('testGuide', 'step1');
}}
>
Complete step 1
</EuiButton>
</EuiTourStep>
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow
label={
<FormattedMessage
id="guidedOnboardingExample.guidesSelection.stepOne.indexNameInputLabel"
defaultMessage="indexName"
/>
}
>
<EuiFieldText value={indexName} onChange={(e) => setIndexName(e.target.value)} />
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow hasEmptyLabelSpace>
<EuiTourStep
content={
<EuiText>
<p>Click this button to complete step 1.</p>
</EuiText>
}
isStepOpen={isTourStepOpen}
minWidth={300}
onFinish={() => setIsTourStepOpen(false)}
step={1}
stepsTotal={1}
title="Step 1"
anchorPosition="rightUp"
>
<EuiButton
onClick={async () => {
await guidedOnboardingApi?.completeGuideStep('testGuide', 'step1', {
indexName,
});
}}
>
Complete step 1
</EuiButton>
</EuiTourStep>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContentBody>
</>
);