diff --git a/src-docs/src/views/tour/not_action_driven.tsx b/src-docs/src/views/tour/not_action_driven.tsx new file mode 100644 index 00000000000..a193a62cb2e --- /dev/null +++ b/src-docs/src/views/tour/not_action_driven.tsx @@ -0,0 +1,147 @@ +import React, { useEffect, useState } from 'react'; + +import { + EuiButton, + EuiButtonEmpty, + EuiSpacer, + EuiTourStep, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiButtonIcon, +} from '../../../../src/components'; + +const demoTourSteps = [ + { + step: 1, + title: 'Preview mode', + content:

See what your project looks like.

, + anchorRef: 'notActionDrivenStep1', + iconType: 'eye', + }, + { + step: 2, + title: 'Build Mode', + content:

Build your project.

, + anchorRef: 'notActionDrivenStep2', + iconType: 'editorCodeBlock', + }, + { + step: 3, + title: 'Comment mode', + content:

Collaborate with your colleagues.

, + anchorRef: 'notActionDrivenStep3', + iconType: 'editorComment', + }, + { + step: 2, + title: 'Share', + content:

Share your project.

, + anchorRef: 'notActionDrivenStep4', + iconType: 'share', + }, +]; + +const tourConfig = { + currentTourStep: 1, + isTourActive: true, + tourPopoverWidth: 360, + tourSubtitle: 'Demo tour', +}; + +const STORAGE_KEY = 'notActionDrivenDemoTour'; + +export default () => { + const [state, setState] = useState(() => { + let initialState: any = localStorage.getItem(STORAGE_KEY); + if (initialState) { + initialState = JSON.parse(initialState); + } else { + initialState = tourConfig; + } + return initialState; + }); + useEffect(() => { + // Store the tour data + localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); + }, [state]); + + const incrementStep = () => { + setState({ + ...state, + currentTourStep: state.currentTourStep + 1, + }); + }; + + const handleClick = () => { + incrementStep(); + }; + + const resetTour = () => { + setState({ + ...state, + currentTourStep: 1, + isTourActive: true, + }); + }; + + const finishTour = () => { + setState({ + ...state, + isTourActive: false, + }); + }; + + return ( +
+ + + {demoTourSteps.map((step, index) => ( + + + Finish tour + + ) : ( + [ + + Close tour + , + + Next + , + ] + ) + } + > + + + + ))} + + + + + Reset tour + +
+ ); +}; diff --git a/src-docs/src/views/tour/tour_example.js b/src-docs/src/views/tour/tour_example.js index c8f85436ae0..2460bdcdcb6 100644 --- a/src-docs/src/views/tour/tour_example.js +++ b/src-docs/src/views/tour/tour_example.js @@ -15,6 +15,7 @@ import Tour from './tour'; import Managed from './managed'; import ManagedHook from './managed_hook'; import FullScreen from './fullscreen'; +import NotActionDriven from './not_action_driven'; import Guidelines from './guidelines'; @@ -44,6 +45,7 @@ const stepDomSource = require('!!raw-loader!./step_dom'); const tourSource = require('!!raw-loader!./tour'); const managedSource = require('!!raw-loader!./managed'); const managedHookSource = require('!!raw-loader!./managed_hook'); +const notActionDriven = require('!!raw-loader!./not_action_driven'); const fullSource = require('!!raw-loader!./fullscreen'); @@ -170,6 +172,24 @@ export const TourExample = { ), demo: , }, + { + title: 'Passive tour', + source: [ + { + type: GuideSectionTypes.TSX, + code: notActionDriven, + }, + ], + text: ( +

+ Use the EuiTour to provide sequential help without + the user performing any actions (e.g. filling out a form or copying a + text). In this scenario, consider using two buttons,{' '} + Close tour and Next. +

+ ), + demo: , + }, { title: 'Fullscreen demo', source: [ diff --git a/src/components/tour/__snapshots__/tour_step.test.tsx.snap b/src/components/tour/__snapshots__/tour_step.test.tsx.snap index acbf0c7ca91..c58a9e158d1 100644 --- a/src/components/tour/__snapshots__/tour_step.test.tsx.snap +++ b/src/components/tour/__snapshots__/tour_step.test.tsx.snap @@ -1,5 +1,103 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`EuiTourStep accepts an array of buttons in the footerAction prop 1`] = ` +
+
+ + Test + +
+
+
+ +
+
+`; + exports[`EuiTourStep can be closed 1`] = `
{ expect(component.render()).toMatchSnapshot(); }); + test('accepts an array of buttons in the footerAction prop', () => { + const component = mount( + {}}>Button 1, + , + ]} + > + Test + + ); + + expect(component.render()).toMatchSnapshot(); + }); + test('can turn off the beacon', () => { const component = mount( diff --git a/src/components/tour/tour_step.tsx b/src/components/tour/tour_step.tsx index 97cd3c36170..8fe6e1500ef 100644 --- a/src/components/tour/tour_step.tsx +++ b/src/components/tour/tour_step.tsx @@ -129,9 +129,10 @@ export type EuiTourStepProps = CommonProps & decoration?: 'none' | 'beacon'; /** - * Element to replace the 'Skip tour' link in the footer + * Accepts any `ReactNode` to replace the 'Skip tour' link in the footer. + * Ideally, pass one button or an array of up to 2 buttons. */ - footerAction?: ReactElement; + footerAction?: ReactNode | ReactNode[]; }; export const EuiTourStep: FunctionComponent = ({ @@ -204,10 +205,29 @@ export const EuiTourStep: FunctionComponent = ({ size: 'xs', }; + const optionalFooterAction: JSX.Element = Array.isArray(footerAction) ? ( + + {footerAction.map((action, index) => ( + + {action} + + ))} + + ) : ( + {footerAction} + ); + const footer = ( 1 ? 'spaceBetween' : 'flexEnd'} + alignItems="center" > {stepsTotal > 1 && ( @@ -228,7 +248,7 @@ export const EuiTourStep: FunctionComponent = ({ )} {footerAction ? ( - {footerAction} + optionalFooterAction ) : (