Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stepper Example Flow: Integrate site creation and state management #99676

Open
wants to merge 43 commits into
base: trunk
Choose a base branch
from

Conversation

agrullon95
Copy link
Contributor

@agrullon95 agrullon95 commented Feb 12, 2025

Related to p1738937572081439-slack-C02T4NVL4JJ

Proposed Changes

  • Update test example flow to use state

Why are these changes being made?

  • Implementing state management and new create site hook in a test flow before adding to production flows

Testing Instructions

  • Checkout branch or use live link
  • Navigate to /setup/example.
  • Verify the sessionID query parameter is added to the URL (should be two random characters).
  • Go through the flow and verify a site is correctly created. You should land on the launchpad at the end.
  • Test free plan, paid plan, free domain, paid domain.

Non-standard but important tests

  1. Picking a paid domain and a free plan, shouldn't take you to checkout, but should add the domain to the cart.
  2. Coming back from Checkout should take you to the plans step (not processing), and the Free plan should be marked with "Your plan".
  3. Going forward again to Checkout shouldn't create another site.
  4. Going forward again to Checkout with a different plan should show the new plan choice.
  5. Going back form Checkout to domains, picking another domain, then checkout again, should show the new domain.

@matticbot
Copy link
Contributor

matticbot commented Feb 12, 2025

Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

App Entrypoints (~255 bytes added 📈 [gzipped])

name           parsed_size           gzip_size
entry-stepper       +900 B  (+0.1%)     +255 B  (+0.1%)

Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used.

Sections (~3407 bytes added 📈 [gzipped])

name                        parsed_size             gzip_size
example-flow                   +25116 B  (+260.4%)    +8031 B  (+231.4%)
site-migration-flow              -530 B    (-1.3%)     -171 B    (-1.4%)
hosted-site-migration-flow       -530 B    (-1.3%)     -171 B    (-1.4%)
async-step-unified-plans         +331 B    (+0.0%)     +125 B    (+0.0%)
update-design-flow               +225 B    (+0.0%)      +80 B    (+0.0%)
build-flow                       +139 B    (+0.0%)      +40 B    (+0.0%)
start-writing-flow               +121 B    (+0.4%)      +38 B    (+0.7%)
site-setup-wg                    +121 B    (+0.0%)      +38 B    (+0.0%)
site-setup-flow                  +121 B    (+0.0%)      +38 B    (+0.0%)
newsletter-flow                  +121 B    (+0.5%)      +37 B    (+0.7%)
design-first-flow                +121 B    (+0.4%)      +29 B    (+0.5%)
copy-site-flow                    +94 B    (+0.0%)      +24 B    (+0.0%)
domains                           +63 B    (+0.0%)      +12 B    (+0.0%)
async-step-unified-domains        +63 B    (+0.0%)      +12 B    (+0.0%)
home                              +22 B    (+0.0%)      +15 B    (+0.0%)
write-flow                        +18 B    (+0.0%)      +10 B    (+0.0%)
site-settings                     +18 B    (+0.0%)      +10 B    (+0.0%)
settings                          +18 B    (+0.0%)      +10 B    (+0.0%)
import-hosted-site-flow           +18 B    (+0.0%)      +11 B    (+0.0%)
hundred-year-plan                 +18 B    (+0.0%)      +10 B    (+0.0%)
hundred-year-domain               +18 B    (+0.0%)      +10 B    (+0.0%)
hosting                           +18 B    (+0.0%)      +10 B    (+0.0%)

Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to.

Async-loaded Components (~12 bytes added 📈 [gzipped])

name                             parsed_size           gzip_size
async-load-signup-steps-domains        +63 B  (+0.0%)      +12 B  (+0.0%)

React components that are loaded lazily, when a certain part of UI is displayed for the first time.

Legend

What is parsed and gzip size?

Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory.
Gzip Size: Compressed size of the JS and CSS files. This much data needs to be downloaded over network.

Generated by performance advisor bot at iscalypsofastyet.com.

@agrullon95 agrullon95 self-assigned this Feb 13, 2025
@agrullon95 agrullon95 requested a review from a team February 14, 2025 09:04
@matticbot matticbot added the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Feb 14, 2025
@agrullon95 agrullon95 marked this pull request as ready for review February 14, 2025 14:14
@agrullon95 agrullon95 requested a review from a team as a code owner February 14, 2025 14:39
@matticbot
Copy link
Contributor

matticbot commented Feb 14, 2025

This PR modifies the release build for the following Calypso Apps:

For info about this notification, see here: PCYsg-OT6-p2

  • notifications
  • wpcom-block-editor

To test WordPress.com changes, run install-plugin.sh $pluginSlug test/stepper-example-flow-state-management on your sandbox.

Copy link
Member

@alshakero alshakero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work. I love it. Left a few comments.

Comment on lines 126 to 128
await saveSiteSettings( providedDependencies?.siteSlug, {
launchpad_screen: 'full',
} );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should bake this into the pending action above. Something like:

createSite( {
	theme: DEFAULT_NEWSLETTER_THEME,
	siteIntent: Onboard.SiteIntent.Newsletter,
} ).then( siteCreationResult => {
   // update site settings but return the siteCreationResult when done.
   return saveSiteSettings( siteCreationResult.siteSlug, {
		launchpad_screen: 'full',
	} ).then(() => siteCreationResult );
} );

To show one loading for all the work.


case 'processing':
if ( providedDependencies?.goToHome && providedDependencies?.siteSlug ) {
return window.location.replace(
addQueryArgs( `/home/${ siteId ?? providedDependencies?.siteSlug }`, {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use siteId from providedDependencies here.

}

if ( siteId ) {
return `/setup/${ flow }/launchpad?siteSlug=${ siteSlug }&siteId=${ siteId }`;
redirectURL = `/setup/${ flow }/launchpad?siteSlug=${ siteSlug }&siteId=${ siteId }`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use addQueryArgs here as well?

@alshakero alshakero changed the base branch from trunk to fix/preload-after-user February 19, 2025 19:41
@alshakero alshakero changed the base branch from fix/preload-after-user to trunk February 20, 2025 14:58
const launchpadUrl = `/setup/${ flowName }/launchpad?siteSlug=${ providedDependencies.siteSlug }`;

switch ( _currentStep ) {
case 'intro':
return navigate( 'newsletterSetup' );

case 'newsletterSetup':
set( 'newsletterSetup', providedDependencies );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need to set( STEP_SLUG, providedDependencies ) for every step, could this be something that the Stepper framework automatically takes care of?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely considered this, but it degrades the clarify of Stepper. Stepper now doesn't manage any state you're not aware of (as a flow owner) so the data flow is quite intuitive. Also, I realized yesterday this hook has a flaw; step slugs are not unique, I'm working on an alternative identifier of the step to use in this hook.

My opinion on not auto calling set is not strong though, especially if we rename submit to updateFlowStateAndSubmit or such.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized yesterday this hook has a flaw; step slugs are not unique, I'm working on an alternative identifier of the step to use in this hook.

That is something that I noticed too (and just discussed with @tyxla earlier today) — could we implement a runtime check when the flow is initialised, that checks if all the steps in a flow have different slugs, and otherwise throws / shows an error? I guess that would also prevent the need for adding an alternative identifier.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re. auto-calling set: I see your point about having flows as explicit as possible. On the other end, relying on the user to reliably call set( STEP_SLUG, providedDependencies ) could also lead to unexpected consequences, on top of making the code more verbose. Maybe the right balance is that the framework automatically calls set for the standard provided deps, and then flow authors can manually call set for any additional dep? Excuses if I'm not making sense, but definitely interesting in learning more about the needs and potential solutions

Copy link
Member

@alshakero alshakero Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great points. Auto-calling set will also make migrating flows much easier.

could we implement a runtime check when the flow is initialised

I'm not worried about flows having steps with non-distinct slugs; I'm really invested into making this hook as strongly typed as possible. And currently it uses the step slug to infer the type of the value it should accept. If the slugs are not unique, we need to find another way to infer the type.

Currently, we have a manifest of key => type here. The keys represent step slugs, and the values are the types of the steps. This is still not ideal because the types are arbitrary and function as hints for linitng, essentially.

I'm considering making another hook (I want to keep this one because it's great for generic state), the other hook would be useStepState and it would accept the whole Step object as key.

Something like

const { setStep, getStep } = useStepState();
setStep( STEPS.INTRO, yourValue );

The great thing about this is that it will infer the types from the step without any manifests, but the issue I'm stuck with is that this key is not serializable, and thus we can't persist it. I'm trying to come up with a way to serialize the step object in a deserializable way.

) }?redirect_to=${ encodeURIComponent( launchpadUrl ) }&signup=1`
// Replace the processing step with checkout step, so going back goes to Plans.
return window.location.replace(
`/checkout/${ encodeURIComponent( siteFragment ) }?redirect_to=${ encodeURIComponent(
Copy link
Contributor Author

@agrullon95 agrullon95 Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checkout URL will cause a shopping cart error because the siteFragment is being picked up as a possible product to add to the cart on the checkout page.

getProductSlugFromContext() Is triggered here and it attempts to add the siteFragment as another product. If it's possible to replace the siteFragment with siteSlug instead, that will solve the issue. If we need siteFragment here ( appears as siteId when I debug) then we can modify the getProductSlugFromContext function.

@alshakero alshakero changed the title Integrate site creation and state management Stepper Example Flow: Integrate site creation and state management Feb 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DO NOT MERGE [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants