Skip to content

Commit

Permalink
Product Editor Onboarding: Show spotlight for first time visitors (#3…
Browse files Browse the repository at this point in the history
…8590)

* Add placeholder TourKit in product-page

* Create component and configure it to show to the right of wp menu

* Finish TourKit and start creating features guide

* Implement CSS changes and background color

* Create Wrapper to facilitate showing the tour and guide without relying on the options in the future

* Refactor
Add tracks events

* Add changelog

* Fix lint problems

* Fix CSS

* Refactor styles

* General refactor and extract BlockEditorGuide component

* Add changelog

* Fix lint issues
  • Loading branch information
nathanss committed Jun 6, 2023
1 parent 7137d60 commit 52fe3c4
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 5 deletions.
5 changes: 5 additions & 0 deletions packages/js/components/changelog/add-block-tour
@@ -0,0 +1,5 @@
Significance: patch
Type: tweak
Comment: Allow passing react elements to heading in TourKit


2 changes: 1 addition & 1 deletion packages/js/components/src/tour-kit/types.ts
Expand Up @@ -12,7 +12,7 @@ export interface WooStep extends Step {
meta: {
/** Unique name for step, mainly used for tracking. */
name: string | null;
heading: string | null;
heading: string | React.ReactElement | null;
descriptions: {
desktop: string | React.ReactElement | null;
mobile?: string | React.ReactElement | null;
Expand Down
12 changes: 8 additions & 4 deletions plugins/woocommerce-admin/client/products/product-page.tsx
Expand Up @@ -17,6 +17,7 @@ import { useParams } from 'react-router-dom';
import { useProductEntityRecord } from './hooks/use-product-entity-record';
import './fills/product-block-editor-fills';
import './product-page.scss';
import BlockEditorTourWrapper from './tour/block-editor/block-editor-tour-wrapper';

declare const productBlockEditorSettings: ProductEditorSettings;

Expand All @@ -36,9 +37,12 @@ export default function ProductPage() {
}

return (
<Editor
product={ product }
settings={ productBlockEditorSettings || {} }
/>
<>
<Editor
product={ product }
settings={ productBlockEditorSettings || {} }
/>
<BlockEditorTourWrapper />
</>
);
}
@@ -0,0 +1,113 @@
/**
* External dependencies
*/

import { Guide } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import './style.scss';

interface Props {
onCloseGuide: () => void;
}

const BlockEditorGuide = ( { onCloseGuide }: Props ) => {
return (
<Guide
className="woocommerce-block-editor-guide"
finishButtonText={ __( 'Close', 'woocommerce' ) }
onFinish={ onCloseGuide }
pages={ [
{
content: (
<>
<h1 className="woocommerce-block-editor-guide__heading">
{ __(
'Refreshed, streamlined interface',
'woocommerce'
) }
</h1>
<p className="woocommerce-block-editor-guide__text">
{ __(
'Experience a simpler, more focused interface with a modern design that enhances usability.',
'woocommerce'
) }
</p>
</>
),
image: (
<div className="woocommerce-block-editor-guide__background1"></div>
),
},
{
content: (
<>
<h1 className="woocommerce-block-editor-guide__heading">
{ __(
'Content-rich product descriptions',
'woocommerce'
) }
</h1>
<p className="woocommerce-block-editor-guide__text">
{ __(
'Create compelling product pages with blocks, media, images, videos, and any content you desire to engage customers.',
'woocommerce'
) }
</p>
</>
),
image: (
<div className="woocommerce-block-editor-guide__background2"></div>
),
},
{
content: (
<>
<h1 className="woocommerce-block-editor-guide__heading">
{ __(
'Improved speed & performance',
'woocommerce'
) }
</h1>
<p className="woocommerce-block-editor-guide__text">
{ __(
'Enjoy a seamless experience without page reloads. Our modern technology ensures reliability and lightning-fast performance.',
'woocommerce'
) }
</p>
</>
),
image: (
<div className="woocommerce-block-editor-guide__background3"></div>
),
},
{
content: (
<>
<h1 className="woocommerce-block-editor-guide__heading">
{ __(
'More features are on the way',
'woocommerce'
) }
</h1>
<p className="woocommerce-block-editor-guide__text">
{ __(
'While we currently support physical products, exciting updates are coming to accommodate more types, like digital products, variations, and more. Stay tuned!',
'woocommerce'
) }
</p>
</>
),
image: (
<div className="woocommerce-block-editor-guide__background4"></div>
),
},
] }
/>
);
};

export default BlockEditorGuide;
@@ -0,0 +1,12 @@
/**
* Internal dependencies
*/
import BlockEditorTour from './block-editor-tour';
import { useBlockEditorTourOptions } from './use-block-editor-tour-options';

const BlockEditorTourWrapper = () => {
const blockEditorTourProps = useBlockEditorTourOptions();
return <BlockEditorTour { ...blockEditorTourProps } />;
};

export default BlockEditorTourWrapper;
@@ -0,0 +1,127 @@
/**
* External dependencies
*/
import { Pill, TourKit } from '@woocommerce/components';
import { __ } from '@wordpress/i18n';
import { recordEvent } from '@woocommerce/tracks';
import { useEffect, useState } from '@wordpress/element';

/**
* Internal dependencies
*/

import './style.scss';
import BlockEditorGuide from './block-editor-guide';

interface Props {
shouldTourBeShown: boolean;
dismissModal: () => void;
}

const BlockEditorTour = ( { shouldTourBeShown, dismissModal }: Props ) => {
useEffect( () => {
if ( shouldTourBeShown ) {
recordEvent( 'block_product_editor_spotlight_view' );
}
}, [ shouldTourBeShown ] );

const [ isGuideOpen, setIsGuideOpen ] = useState( false );

const openGuide = () => {
setIsGuideOpen( true );
};

const closeGuide = () => {
recordEvent( 'block_product_editor_spotlight_completed' );
setIsGuideOpen( false );
};

if ( isGuideOpen ) {
return <BlockEditorGuide onCloseGuide={ closeGuide } />;
} else if ( shouldTourBeShown ) {
return (
<TourKit
config={ {
steps: [
{
meta: {
name: 'woocommerce-block-editor-tour',
primaryButton: {
text: __(
'View highlights',
'woocommerce'
),
},
descriptions: {
desktop: __(
"We designed a brand new product editing experience to let you focus on what's important.",
'woocommerce'
),
},
heading: (
<>
<span>
{ __(
'Meet a streamlined product form',
'woocommerce'
) }
</span>{ ' ' }
<Pill className="woocommerce-block-editor-guide__pill">
{ __( 'Beta', 'woocommerce' ) }
</Pill>
</>
),
},
referenceElements: {
desktop: '#adminmenuback',
},
},
],
closeHandler: ( _steps, _currentStepIndex, source ) => {
dismissModal();
if ( source === 'done-btn' ) {
recordEvent(
'block_product_editor_spotlight_view_highlights'
);
openGuide();
} else {
recordEvent(
'block_product_editor_spotlight_dismissed'
);
}
},
options: {
effects: {
arrowIndicator: false,
overlay: false,
liveResize: {
rootElementSelector: '#adminmenuback',
resize: true,
},
},
portalParentElement:
document.getElementById( 'wpbody' ),
popperModifiers: [
{
name: 'bottom-left',
enabled: true,
phase: 'beforeWrite',
requires: [ 'computeStyles' ],
fn: ( { state } ) => {
state.styles.popper.top = 'auto';
state.styles.popper.left = 'auto';
state.styles.popper.bottom = '10px';
state.styles.popper.transform =
'translate3d(10px, 0px, 0px)';
},
},
],
},
} }
/>
);
}
return null;
};

export default BlockEditorTour;
@@ -0,0 +1,58 @@
$background-height: 220px;
$yellow: #f5e6ab;

.woocommerce-block-editor-guide {
&__background1 {
height: $background-height;
background-color: #f2edff;
}
&__background2 {
height: $background-height;
background-color: #dfd1fb;
}
&__background3 {
height: $background-height;
background-color: #cfb9f6;
}
&__background4 {
height: $background-height;
background-color: #bea0f2;
}
&__pill {
border: 1px solid $yellow;
background-color: $yellow;
}
&.components-modal__frame {
max-width: 320px;
}
&__heading,
&__text {
margin: $gap-small $gap-large;
}

&__heading {
font-size: 24px;
line-height: 30px;
}

.components-guide {
&__page-control {
margin-top: $gap-small;
}
&__footer {
width: unset;
margin: $gap-large $gap-large;
.components-guide__back-button {
left: 0;
padding: $gap-smaller $gap-small;
}
.components-guide__forward-button {
right: 0;
padding: $gap-smaller $gap-small;
}
.components-guide__finish-button {
right: 0;
}
}
}
}
@@ -0,0 +1,37 @@
/**
* External dependencies
*/
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { useSelect, useDispatch } from '@wordpress/data';

export const BLOCK_EDITOR_TOUR_SHOWN_OPTION =
'woocommerce_block_product_tour_shown';

export const useBlockEditorTourOptions = () => {
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME );
const { shouldTourBeShown } = useSelect( ( select ) => {
const { getOption, hasFinishedResolution } =
select( OPTIONS_STORE_NAME );

const wasTourShown =
getOption( BLOCK_EDITOR_TOUR_SHOWN_OPTION ) === 'yes' ||
! hasFinishedResolution( 'getOption', [
BLOCK_EDITOR_TOUR_SHOWN_OPTION,
] );

return {
shouldTourBeShown: ! wasTourShown,
};
} );

const dismissModal = () => {
updateOptions( {
[ BLOCK_EDITOR_TOUR_SHOWN_OPTION ]: 'yes',
} );
};

return {
dismissModal,
shouldTourBeShown,
};
};
4 changes: 4 additions & 0 deletions plugins/woocommerce/changelog/add-block-tour
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Show spotlight for first time visitors of block product editor

0 comments on commit 52fe3c4

Please sign in to comment.