-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Guided onboarding: add intent based plans #90809
Conversation
Jetpack Cloud live (direct link)
Automattic for Agencies live (direct link)
|
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser: Sections (~235 bytes added 📈 [gzipped])
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 (~782 bytes added 📈 [gzipped])
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. Generated by performance advisor bot at iscalypsofastyet.com. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is context being introduced into the grid, which i'm not certain it needs to live in the package. Left a comment in the code to get started, but need to take a closer.
@@ -26,6 +26,7 @@ export interface UseGridPlansParams { | |||
* Provide a map of plan slug keyed strings to display as the highlight label on top of each plan. | |||
*/ | |||
highlightLabelOverrides?: { [ K in PlanSlug ]?: TranslateResult }; | |||
segmentationAnswers?: { [ key: string ]: string[] }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's clarify what this property is and why it needs to be introduced in the grid. Also the signature is too open ended. Have you considered defining a more concrete type for the values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hook needs to go/live outside of plans-grid-next
. Part of our goal with this package is to keep it as lean as possible, with context like "answers to a survey" not really being part of anything. Also, any new properties, whether on the grid components or its helpers/hooks to be of generic nature, not context specific (e.g. segmentationAnswers
is of the latter).
I think there are a couple of options:
- Define a
useIntentFromSegmentationAnswers
and return a differentPlansIntent
for every combination of plan types - so you'd be returningplans-guided-default
,plans-guided-blogger
,plans-guided-consumer
, etc. Hopefully, this also serves as a signal to define a betterSegment
type for the survey work - cause it's all strings right now. You'd introduce those intents in the main switch block ofuse-grid-plans
to define the plan type combinations (as done for everything else - no other logic/change there). The hook would live either near the logic or somewhere else in Calypso e.g. inplans-features-main
. - Define a
usePlanTypesWithIntentOverride( { intent } )
(the name kinda slips to me now) and pass that into the hooks. In there, you can call this hook and return whatever type combination for the "one" intent defined. We'd somehow work that into the grid hooks as an optional override.
I'll give it some more thought/perspective, but these are options, even if intermediate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p.s. the difference between multiple intents and single intent doesn't seem to be significant right now. So either option doesn't seem to deviate too far. If in a later scenario, you consider "flow divergence" (so diverging to a different flow on every answer) - which would give you a different "site intent" in the end (I think!) - then the first option is probably better due to mapping across to "plan intent" already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @Automattic/martech-decepticons
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What @chriskmnds suggested above should already work pretty well. Fwiw I'd like to throw in another option to consider :)
The general idea of the classic signup framework is that it's a pipeline filling up the required "depedency data" through calls to the submitSignupStep function, e.g. how the domains step does it. That also means a former step can produce data that can be consumed by a latter step to act on accordingly. e.g. again see how the domain step gets the design type data set by a former step so it can pick different TLDs accordingly.
So, if the segmentation survey was a new signup step, one more way to do it is to:
- Let it submit a new dependency data entry, e.g.
segmentationSurveyAnswers
. - In the plans step component, derive the intent in some ways using
segmentaionSurveyAnswers
in theplansFeaturesList
function before feeding it into PlansFeaturesMain: https://github.com/Automattic/wp-calypso/blob/trunk/client/signup/steps/plans/index.jsx#L144.
A slight benefit of doing this way is that the step submission will be tracked by default using calypso_signup_step_submit
event, so you won't need to setup additional tracking.
I don't have strong preference on either way; just want to raise another option here in case it helps :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@southp yep. it sounds like a good way to go (keep it near the step logic and pass it into PlansFeaturesMain
via the prop)
The hook would live either near the logic or somewhere else in Calypso e.g. in plans-features-main.
I think what you outlined would precisely cover the "near the logic" part 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great idea. I would love to submit the survey answers via the submitSignupStep
function in a next PR. I don't love the fact that we have to post and fetch between step 1 and 3.
TYPE_PERSONAL, | ||
TYPE_PREMIUM, | ||
} from '@automattic/calypso-products'; | ||
import useGuidedFlowGetSegment from './use-guided-flow-get-segment'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess still WIP? This doesn't exist in the package, right❓ (I hope not, we'd need to refactor out now 😬 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still WIP yes, the hook now lives in another part https://github.com/Automattic/wp-calypso/pull/91017/files.
@agrullon95, @chriskmnds is right, as our hook to retrieve the segment is not correlated to the plans, but to be used in various places, so we need to find another place for it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe packages/onboarding
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe that's fine?
We won't be querying/using that package in plans-grid-next
either, so these still need to be pulled in differently here. The package is imported here, but I think it should be safe to remove - it's only for some styles that we may have cleaned up already.
…ypso into add/intent-based-plans
const { segment: intentFromSegmentationSurvey, isLoadingSegment } = useSegmentedIntent( | ||
flowName === 'guided', | ||
siteId | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may be missing something but is there anything blocking from moving this call to the consumer of plans-features-main
and avoid the unnecessary logic/conditioning in the main hook below? Just pass the intent you want into plans-features-main
. That's what the intent
prop is meant for.
You can still call it here to get the isLoadiingSegment
state if that's meaningful to anything, but I'd rather we avoid intentFromSegmentationSurvey
being defined explicitly here. You are overriding the intentFromProps
indirectly with this. I would not expect any changes to the useEffect
hook below 🤷♂️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's when I started, really. But the parent component is a class component, and doing network requests there sucks. I'd have to make Redux actions, selector, reducer, etc... I can do all of these, but IMHO the trade-off is not worth it. 4 lines vs a bunch of files.
You are overriding the intentFromProps indirectly with this.
That's a great point! I wasn't worried about it because the hook only engages for this flow. But it's still code smell to ignore props.
How about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Omar. I haven't had a chance to check the last update. Concerning the class component though, you can write a simple HOC in that case, call the hook and propagate the value in the props. It can all be done in the same file, 3-4 lines wrapping the connected component, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ that's been our strategy for cases in plans-grid that we needed the interplay of hooks and classes, before refactoring everything to function form
I think here you can find examples of a wrapper component or hoc to follow:
const WrappedPlanFeatures2023Grid = ( props: PlanFeatures2023GridType ) => { |
Love how much simpler this got |
const [ intent, setIntent ] = useState< PlansIntent | undefined >( undefined ); | ||
|
||
const { segment: intentFromSegmentationSurvey, isLoadingSegment } = useSegmentedIntent( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we are deriving the intent value according to the segmentation survey here. There are two main drawbacks here:
- Once a site is created, the intent value will be stored in the stie meta. e.g. that's how a site created from
/setup/newsletter
will see the same plan mix at the logged-in/plans
. Ideally all the survey data handling should only happen in the signup flow. After that, the site should just use the stored meta data. - An extra
GET
request to the survey endpoint will be fired everytime<PlansFeaturesMain>
is rendered no matter it's needed or not, e.g. an existing account without ever seeing the survey visits/plans
.
I have a feeling that it might be something that can be addressed together with @chriskmnds 's comment below since it's likely also about pulling this to the consumer end. If not, it'd be wonderful if we can address as a follow-up :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally I see the light. Thank you so much @southp and @chriskmnds! Pushed.
…rvey-answers-query.test.tsx
Note: don't merge because this introduces a regression. The survey answers are now carried over from the guided flow into the following visits of the plans grid in other flows. Edit: Fixed. |
const { siteUrl, domainItem, siteTitle, username, coupon } = signupDependencies; | ||
const { siteUrl, domainItem, siteTitle, username, coupon, segmentationSurveyAnswers } = | ||
signupDependencies; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TBD: | |
// The reason that we need to narrow down the effect here by checking the flow name is because the signup progress is shared among flows. However, that behavior is problematic here | |
// It used to cause another issue here: https://github.com/Automattic/wp-calypso/pull/74329 | |
// We need to find a better way to define a signup session with cleaner boundary of shared storage. |
Great catch. It's a framework-limitation that will need some more careful thoughts to fix, so this looks like a good workaround for now. I've also suggested to add an inlined TBD note for posterity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @southp. We will test this PR for regressions and then probably merge on Monday
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, @escapemanuele . To better track this, I've created a tracking issue for it: #91377. Thanks to the work here, we now have one solid instance to check when we propose a solution to it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the issue here stems from the solution/approach, not the framework's limitation (which does exist, but it's kinda concequential that it surfaces).
If plans-intent was a property on the flow, not something computed dynamically, then we'd propably not need to bother at all. So at a higher level, instead of deriving intent dynamically, send user to a different flow that has that intent configured. I proposed this earlier as "flow divergence", and seeing that it's only relevant for a couple of intents, then it sounds more tempting now, unless any other underlying concerns.
‐--------
p.s. #91377 brings a bit of a "fix the symptom" vibe to me, but will comment there 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried about the complexity that will emerge in tracks and the A/B/C experiment we'll run if we split this into 5 flows. I'll merge this, but please don't take it as the end of accepting (and appreciating) feedback!
solid. thanks for the patience, refactors, and eventual enlightenment. not touching plans-grid-next
or plans-features-main
makes it for a happy ending :-)
Related to 7019-gh-Automattic/dotcom-forge
Proposed Changes
It should follow this table
P2 Segment to Plan mapping: pdDR7T-1xi-p2
Why are these changes being made?
See pdDR7T-1xi-p2
Testing Instructions