Skip to content

Commit 3cc4f77

Browse files
added getting started
1 parent fe122d0 commit 3cc4f77

File tree

11 files changed

+265
-40
lines changed

11 files changed

+265
-40
lines changed

plugin.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ function is_frontend() {
269269
/**
270270
* Welcome screen.
271271
*/
272+
require_once( plugin_dir_path( __FILE__ ) . 'src/welcome/getting-started.php' );
272273
if ( is_admin() ) {
273274
require_once( plugin_dir_path( __FILE__ ) . 'src/welcome/index.php' );
274275
require_once( plugin_dir_path( __FILE__ ) . 'src/welcome/news.php' );

src/components/guided-modal-tour/index.js

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
* Internal dependencies
33
*/
44
import { TOUR_STEPS } from './tour-steps'
5+
import './tour-trigger'
56

67
/**
78
* External dependencies
89
*/
910
import {
1011
i18n,
11-
guidedTourStates, // TODO: This doesn't exist yet. The state should be loaded here from localize values, this should be an object with the tour ID as the key and the state as the value.
12+
guidedTourStates,
1213
} from 'stackable'
1314
import classNames from 'classnames'
1415
import confetti from 'canvas-confetti'
@@ -19,6 +20,7 @@ import confetti from 'canvas-confetti'
1920
import {
2021
Modal, Flex, Button,
2122
} from '@wordpress/components'
23+
import { models } from '@wordpress/api'
2224
import { __ } from '@wordpress/i18n'
2325
import {
2426
Icon, arrowRight, arrowLeft, info,
@@ -36,14 +38,21 @@ const GuidedModalTour = props => {
3638
} = props
3739

3840
// On mount, check if the tour has been completed, if so, don't show it.
39-
const [ isDone, setIsDone ] = useState( guidedTourStates?.[ tourId ] )
41+
const [ isDone, setIsDone ] = useState( guidedTourStates.includes( tourId ) )
42+
43+
// We need this to prevent the tour from being shown again if it's just completed.
44+
const [ justCompleted, setJustCompleted ] = useState( false )
4045

4146
const {
4247
steps = [],
4348
condition = null,
4449
hasConfetti = true,
4550
} = TOUR_STEPS[ tourId ]
4651

52+
if ( justCompleted ) {
53+
return null
54+
}
55+
4756
// If there is a condition, check if it's met, if not, don't show the tour.
4857
// condition can be true, false, or null. true will show the tour (even if
4958
// it's already done), false will not show the tour, null will show the tour
@@ -65,8 +74,23 @@ const GuidedModalTour = props => {
6574
steps={ steps }
6675
hasConfetti={ hasConfetti }
6776
onClose={ () => {
68-
// TODO: Save the tour state to the database that we finished it.
6977
setIsDone( true )
78+
setJustCompleted( true )
79+
80+
// Update the stackable_guided_tour_states setting
81+
if ( ! guidedTourStates.includes( tourId ) ) {
82+
// eslint-disable-next-line camelcase
83+
const settings = new models.Settings( { stackable_guided_tour_states: [ ...guidedTourStates, tourId ] } )
84+
settings.save()
85+
}
86+
87+
// Soft update the global variable to prevent the tour from being shown again.
88+
guidedTourStates.push( tourId )
89+
90+
// Remove the "tour" GET parameter from the URL so conditions won't get triggered again.
91+
const url = new URL( window.location.href )
92+
url.searchParams.delete( 'tour' )
93+
window.history.replaceState( null, '', url.toString() )
7094
} }
7195
/>
7296
}
@@ -331,6 +355,21 @@ const ModalTour = props => {
331355
}
332356
}, [ glowTarget, currentStep, isVisible, isVisibleDelayed, isTransitioning, forceRefresh ] )
333357

358+
// When unmounted, do not call onClose. So we need to do this handler on our own.
359+
useEffect( () => {
360+
const handleHeaderClick = () => {
361+
onClose()
362+
}
363+
if ( modalRef.current ) {
364+
modalRef.current.querySelector( '.components-modal__header' ).addEventListener( 'click', handleHeaderClick )
365+
}
366+
return () => {
367+
if ( modalRef.current ) {
368+
modalRef.current.querySelector( '.components-modal__header' ).removeEventListener( 'click', handleHeaderClick )
369+
}
370+
}
371+
}, [ modalRef.current, onClose ] )
372+
334373
if ( ! isVisible ) {
335374
return null
336375
}
@@ -341,7 +380,7 @@ const ModalTour = props => {
341380
overlayClassName="ugb-tour-modal--overlay"
342381
shouldCloseOnClickOutside={ false }
343382
size={ size }
344-
onRequestClose={ onClose }
383+
// onRequestClose={ onClose } // Do not use onRequestClose, it will cause the tour finish
345384
className={ classNames( 'ugb-tour-modal', `ugb-tour-modal--${ position }`, {
346385
'ugb-tour-modal--visible': isVisible,
347386
'ugb-tour-modal--visible-delayed': isVisibleDelayed,

src/components/guided-modal-tour/tour-steps.js

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,33 @@ import { createInterpolateElement } from '@wordpress/element'
4747
*/
4848

4949
export const TOUR_STEPS = {
50-
'design-system-welcome': {
50+
editor: {
51+
hasConfetti: false,
52+
condition: () => { // If provided, true will show the tour (even if it's already done), false will not show the tour, null will show the tour only once.
53+
// Do not show the tour if there is a GET parameter that shows another tour.
54+
return window?.location?.search?.includes( 'tour=' ) ? false : null
55+
},
56+
steps: [
57+
{
58+
title: '👋 ' + __( 'Welcome to Stackable', i18n ),
59+
description: __( 'We\'re excited to have you here. Let\'s get you started by opening the Design Library.', i18n ),
60+
help: createInterpolateElement( __( 'Click the <strong>Design Library</strong> button to continue.', i18n ), {
61+
strong: <strong />,
62+
} ),
63+
// size: 'medium',
64+
anchor: '.ugb-insert-library-button',
65+
position: 'bottom',
66+
nextEventTarget: '.ugb-insert-library-button',
67+
glowTarget: '.ugb-insert-library-button',
68+
showNext: false,
69+
},
70+
],
71+
},
72+
'design-system': {
73+
condition: () => { // If provided, true will show the tour (even if it's already done), false will not show the tour, null will show the tour only once.
74+
// Force show the tour if there is a GET parameter tour=design-system
75+
return window?.location?.search?.includes( 'tour=design-system' ) ? true : null
76+
},
5177
steps: [
5278
{
5379
title: '👋 ' + __( 'Welcome to Your Design System', i18n ),
@@ -77,33 +103,10 @@ export const TOUR_STEPS = {
77103
// TODO: this is not yet finished
78104
],
79105
},
80-
'editor-welcome': {
81-
hasConfetti: false,
82-
condition: () => { // If provided, true will show the tour (even if it's already done), false will not show the tour, null will show the tour only once.
83-
// Do not show the tour if there is a GET parameter that shows another tour.
84-
return window?.location?.search?.includes( 'tour=' ) ? false : null
85-
},
86-
steps: [
87-
{
88-
title: '👋 ' + __( 'Welcome to Stackable', i18n ),
89-
description: __( 'We\'re excited to have you here. Let\'s get you started by opening the Design Library.', i18n ),
90-
help: createInterpolateElement( __( 'Click the <strong>Design Library</strong> button to continue.', i18n ), {
91-
strong: <strong />,
92-
} ),
93-
// size: 'medium',
94-
anchor: '.ugb-insert-library-button',
95-
position: 'bottom',
96-
nextEventTarget: '.ugb-insert-library-button',
97-
glowTarget: '.ugb-insert-library-button',
98-
showNext: false,
99-
},
100-
],
101-
},
102-
'design-library-welcome': {
106+
'design-library': {
103107
condition: () => { // If provided, true will show the tour (even if it's already done), false will not show the tour, null will show the tour only once.
104-
// TODO: The new quick button in the getting started area should open the editor with `tour=design-library-welcome`
105-
// Force show the tour if there is a GET parameter tour=design-library-welcome
106-
return window?.location?.search?.includes( 'tour=design-library-welcome' ) ? true : null
108+
// Force show the tour if there is a GET parameter tour=design-library
109+
return window?.location?.search?.includes( 'tour=design-library' ) ? true : null
107110
},
108111
steps: [
109112
{
@@ -169,4 +172,28 @@ export const TOUR_STEPS = {
169172
},
170173
],
171174
},
175+
'site-kit': {
176+
condition: () => { // If provided, true will show the tour (even if it's already done), false will not show the tour, null will show the tour only once.
177+
// Force show the tour if there is a GET parameter tour=site-kit
178+
return window?.location?.search?.includes( 'tour=site-kit' ) ? true : null
179+
},
180+
steps: [
181+
{
182+
title: '👋 ' + __( 'Welcome to Site Kits', i18n ),
183+
description: '', // Not yet available.
184+
},
185+
],
186+
},
187+
'design-system-picker': {
188+
condition: () => { // If provided, true will show the tour (even if it's already done), false will not show the tour, null will show the tour only once.
189+
// Force show the tour if there is a GET parameter tour=design-system-picker
190+
return window?.location?.search?.includes( 'tour=design-system-picker' ) ? true : null
191+
},
192+
steps: [
193+
{
194+
title: '👋 ' + __( 'Welcome to The Design System Picker', i18n ),
195+
description: '', // Not yet available.
196+
},
197+
],
198+
},
172199
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* This is a plugin that triggers a guided modal tour.
3+
* It is used to trigger a guided modal tour when a user visits a page with a GET parameter `tour=tourId`.
4+
* This is also in charge of clicking/opening things needed for the tour.
5+
*/
6+
7+
import { registerPlugin } from '@wordpress/plugins'
8+
import { useEffect } from '@wordpress/element'
9+
import { dispatch } from '@wordpress/data'
10+
11+
const TourTrigger = () => {
12+
useEffect( () => {
13+
// Check th
14+
const tourId = window?.location?.search?.includes( 'tour=' ) ? window?.location?.search?.split( 'tour=' )[ 1 ] : null
15+
switch ( tourId ) {
16+
case 'editor':
17+
// When the editor tour is to be shown, no need to do anything here.
18+
break
19+
case 'design-library':
20+
// When the design library tour is to be shown, simluate a click on the design library button.
21+
setTimeout( () => {
22+
document.querySelector( '.ugb-insert-library-button' )?.click()
23+
}, 500 )
24+
break
25+
case 'design-system':
26+
// When the design system tour is to be shown, open the sidebar
27+
setTimeout( () => {
28+
dispatch( 'core/edit-post' )?.openGeneralSidebar( 'stackable-global-settings/sidebar' ) // For Block Editor
29+
}, 500 )
30+
}
31+
}, [] )
32+
33+
return null
34+
}
35+
36+
registerPlugin( 'stackable-guided-modal-tour-trigger', {
37+
render: TourTrigger,
38+
icon: () => null,
39+
showInSidebar: false,
40+
} )

src/components/modal-design-library/modal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ export const ModalDesignLibrary = props => {
217217
>
218218
<div className="ugb-modal-design-library__wrapper">
219219

220-
<GuidedModalTour tourId="design-library-welcome" />
220+
<GuidedModalTour tourId="design-library" />
221221

222222
<aside className="ugb-modal-design-library__sidebar">
223223
<div className="ugb-modal-design-library__filters">

src/plugins/design-library-button/design-library-button.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const DesignLibraryButton = () => {
4444
return ( settings.stackable_enable_design_library &&
4545
<>
4646
{ /* TODO: This will need to only play on first time going to the editor, and if there's no quick button that started another tour */ }
47-
<GuidedModalTour tourId="editor-welcome" />
47+
<GuidedModalTour tourId="editor" />
4848
<ToolbarButton
4949
onClick={ onClick }
5050
className="ugb-insert-library-button"

src/plugins/global-settings/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ const GlobalSettings = () => {
9393
<StyleGuidePopover onClose={ () => setIsStyleGuideOpen( false ) } />
9494
) }
9595
{ globalSettingsInspector }
96-
<GuidedModalTour tourId="design-system-welcome" />
96+
<GuidedModalTour tourId="design-system" />
9797
</PluginSidebar>
9898
}
9999
</>

src/welcome/getting-started.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import SVGCommunityIcon from './images/user.svg'
88
import SVGArrowUpRightIcon from './images/arrow-up-right.svg'
99
import SVGDivider from './images/divider.svg'
1010
import SVGQuickButtonsArrow from './images/quick-buttons-arrow.svg'
11+
import SVGCheck from './images/check.svg'
1112

1213
/**
1314
* WordPress dependencies
@@ -24,7 +25,8 @@ import {
2425
/**
2526
* External dependencies
2627
*/
27-
import { i18n } from 'stackable'
28+
import { i18n, guidedTourStates } from 'stackable'
29+
import classNames from 'classnames'
2830

2931
const generalProps = [
3032
{
@@ -134,7 +136,15 @@ export const GettingStarted = () => {
134136
<p>{ __( 'Jump straight into our Design Library and insert polished, pre-built sections — no more blank-page overwhelm.', i18n ) }</p>
135137
</div>
136138
<div className="s-quick-button-button">
137-
<a href="/wp-admin/post-new.php?post_type=page&tour=design-library-welcome" className="s-button s-secondary-button uppercase">
139+
<a
140+
href={ `/wp-admin/post-new.php?post_type=page&content=${ __( 'Welcome to Stackable', i18n ) }&tour=design-library` }
141+
className={ classNames( 's-button s-secondary-button uppercase', {
142+
's-button--checked': guidedTourStates?.includes( 'design-library' ),
143+
} ) }
144+
>
145+
<span className="s-quick-button-toggle-indicator">
146+
<SVGCheck />
147+
</span>
138148
{ __( 'Build Now', i18n ) }
139149
</a>
140150
</div>
@@ -149,7 +159,15 @@ export const GettingStarted = () => {
149159
<p>{ __( 'Set your brand\'s colors, fonts, and spacing once in the Design System — every Stackable block updates automatically.', i18n ) }</p>
150160
</div>
151161
<div className="s-quick-button-button">
152-
<a href="/wp-admin/post-new.php?post_type=page&tour=design-system-welcome" className="s-button s-secondary-button uppercase">
162+
<a
163+
href={ `/wp-admin/post-new.php?post_type=page&content=${ __( 'Welcome to Stackable', i18n ) }&tour=design-system` }
164+
className={ classNames( 's-button s-secondary-button uppercase', {
165+
's-button--checked': guidedTourStates?.includes( 'design-system' ),
166+
} ) }
167+
>
168+
<span className="s-quick-button-toggle-indicator">
169+
<SVGCheck />
170+
</span>
153171
{ __( 'Try Now', i18n ) }
154172
</a>
155173
</div>
@@ -165,7 +183,12 @@ export const GettingStarted = () => {
165183
<p>{ __( 'Pick a ready-made website template to kickstart your project — fully built layouts and styles, just swap in your content.', i18n ) }</p>
166184
</div>
167185
<div className="s-quick-button-button">
168-
<a href="/wp-admin/post-new.php?post_type=page" className="s-button s-secondary-button uppercase">
186+
<a
187+
href="/wp-admin/post-new.php?post_type=page"
188+
className={ classNames( 's-button s-secondary-button uppercase', {
189+
's-button--checked': guidedTourStates?.includes( 'site-kit' ),
190+
} ) }
191+
>
169192
{ __( 'Select Kit', i18n ) }
170193
</a>
171194
</div>
@@ -181,7 +204,12 @@ export const GettingStarted = () => {
181204
<p>{ __( 'Browse curated Design System presets — apply a professional look instantly without building from scratch.', i18n ) }</p>
182205
</div>
183206
<div className="s-quick-button-button">
184-
<a href="/wp-admin/post-new.php?post_type=page" className="s-button s-secondary-button uppercase">
207+
<a
208+
href="/wp-admin/post-new.php?post_type=page"
209+
className={ classNames( 's-button s-secondary-button uppercase', {
210+
's-button--checked': guidedTourStates?.includes( 'design-system-picker' ),
211+
} ) }
212+
>
185213
{ __( 'Pick A System', i18n ) }
186214
</a>
187215
</div>

0 commit comments

Comments
 (0)