Skip to content

Dashboard v2: lazy load current plan (expiry + manage link) #103601

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

Merged
merged 12 commits into from
May 27, 2025

Conversation

ellatrix
Copy link
Contributor

@ellatrix ellatrix commented May 21, 2025

Proposed Changes

  • Nothing changes for free plans.
  • Lazy load the current plan, which is needed for the manage link and the expiry date.
  • While the plan is loading, gracefully show a blurred text and link. In reality, this is so fast gone that it's not even visible, but just in case to prevent layout shifts.
  • What's more is that after the first time this data is cached for 24h, so it's instant in those cases.
  • To minimise the loading state even more, we prefetch the resource for preloading (while not letting it block the page request)
  • Perhaps one would consider this essential information and required to be in the loader, if so we can discuss it here. I'm on the fence myself, it was much clearer for the other cases.
  • Also adds a <time> tag for the date when it's loaded.
Screenshot 2025-05-21 at 19 15 46

Why are these changes being made?

Testing Instructions

Pre-merge Checklist

  • Has the general commit checklist been followed? (PCYsg-hS-p2)
  • Have you written new tests for your changes?
  • Have you tested the feature in Simple (P9HQHe-k8-p2), Atomic (P9HQHe-jW-p2), and self-hosted Jetpack sites (PCYsg-g6b-p2)?
  • Have you checked for TypeScript, React or other console errors?
  • Have you used memoizing on expensive computations? More info in Memoizing with create-selector and Using memoizing selectors and Our Approach to Data
  • Have we added the "[Status] String Freeze" label as soon as any new strings were ready for translation (p4TIVU-5Jq-p2)?
    • For UI changes, have we tested the change in various languages (for example, ES, PT, FR, or DE)? The length of text and words vary significantly between languages.
  • For changes affecting Jetpack: Have we added the "[Status] Needs Privacy Updates" label if this pull request changes what data or activity we track or use (p4TIVU-aUh-p2)?

@ellatrix ellatrix requested review from youknowriad and a team as code owners May 21, 2025 17:40
@matticbot matticbot added [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels May 21, 2025
Copy link

github-actions bot commented May 21, 2025

@matticbot
Copy link
Contributor

matticbot commented May 21, 2025

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

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

name                    parsed_size           gzip_size
entry-dashboard-dotcom        +19 B  (+0.0%)       +8 B  (+0.0%)
entry-dashboard-a4a           +19 B  (+0.0%)       +8 B  (+0.0%)

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

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.

Copy link
Contributor

@fushar fushar left a comment

Choose a reason for hiding this comment

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

Lazy load the current plan
Perhaps one would consider this essential information and required to be in the loader, if so we can discuss it here. I'm on the fence myself, it was much clearer for the other cases.

I think it's fine for it to load async in the overview page, given its location at the bottom of the page, where my eyes would take time themselves to reach to that location.

Maybe a better way in the future, is to somehow preload/prefetch all required data for the site overview page, when we hover on the site from the grid, so by the time we click and land on the overview, they are all ready.

/* translators: %s: date of plan's expiration date. Eg. August 20, 2025 */
const expiresString = __( 'Expires on <time>%s</time>.' );

// Show a blurred time while we wait for the current plan.
Copy link
Contributor

Choose a reason for hiding this comment

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

This assumes that every plan has expiry, which is not true. For example, sites created via A4A won't have that, which breaks in this PR:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is the value in that case?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's null in such cases.

I believe it's already reflected in the type:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would be undefined, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I fixed it in ad4fcee

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I blurred the entire line instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's semantically wrong, because the expiration / subscription actually exists but is managed by A4A 🥲

Wait, so doesn't the "manage subscription" link then also not make sense? I'm curious what info there is in the site.plan when it's an A4A site.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah right... seems so. BTW, this is how the site overview looks for A4A site in v1:

image

Perhaps we shouldn't worry about it now...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do you check if it's managed? Is that in the plan? I don't have a site that is a4a atm, not sure how to test

Copy link
Contributor

Choose a reason for hiding this comment

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

Something like this 🙈

if ( isAgencyPartnerType( purchase.partnerType ) ) {
return i18n.translate( 'Agency Managed Plan' );
}

@ellatrix
Copy link
Contributor Author

given its location at the bottom of the page

Looking at the new design explorations, this may change though.

Maybe a better way in the future, is to somehow preload/prefetch all required data for the site overview page, when we hover on the site from the grid, so by the time we click and land on the overview, they are all ready.

Interesting, so you're basically suggesting two types of resources:

  • Ones that should block page navigation.
  • Ones that can be prefetched, but shouldn't necessarily block page navigation, it's just nice if they're ready.

@ellatrix
Copy link
Contributor Author

@fushar That's a cool suggestion, I implemented it in 55f89b5

@matticbot
Copy link
Contributor

matticbot commented May 22, 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 try/lazy-current-plan on your sandbox.

/* translators: %s: date of plan's expiration date. Eg. August 20, 2025 */
const expiresString = __( 'Expires on <time>%s</time>.' );

// Show a blurred time while we wait for the current plan.
Copy link
Contributor

Choose a reason for hiding this comment

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

But it means there will still be layout shift for such sites.

Maybe that's OK for now; in general I believe A4A sites should be handled / shown differently in the overview page.

/* translators: %s: date of plan's expiration date. Eg. August 20, 2025 */
const expiresString = __( 'Expires on <time>%s</time>.' );

// Show a blurred time while we wait for the current plan.
Copy link
Contributor

Choose a reason for hiding this comment

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

That would be undefined, no?

I always forgot the difference between ? and null 🤦

// Some plans don't have an expiry date, and we don't want to show it at
// all.
if ( ! currentPlan.expiry ) {
return __( 'No expiration date.' );
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's return nothing here as per the comment above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but then there will be a layout shift. I'll look into properly handling it when it's a4a managed.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, I was just going to suggest to do it separately, not sure if it's worth handling it now since it's very likely that the design will change for A4A sites.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, sure, let's do that. I think the new design has it in card as well, so in that case it would be fixed height and now something that could cause a shift.

Copy link
Contributor

@fushar fushar left a comment

Choose a reason for hiding this comment

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

Re-tested 👍

@@ -40,3 +40,10 @@ h1 {
font-family: var( --dashboard-h1__font-family );
font-weight: var( --dashboard-h1__font-weight ) !important;
}

.is-blurred {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is way more versatile, can be used with any element.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not 100% confident with this; this seems to be the first "free-style" CSS in the dashboard root code. Wanted to get input from @youknowriad .

After contemplating, it seems that the "canonical" way is to implement this on <Text>, something like <Text variant="blurred">. But of course that's not going to happen 😄

Copy link
Contributor

@youknowriad youknowriad May 22, 2025

Choose a reason for hiding this comment

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

I prefer a component over reusing a CSS indeed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should avoid utility CSS as it quickly becomes non easily manageable (disconnect between usage and declaration causing dead code over time...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After contemplating, it seems that the "canonical" way is to implement this on <Text>

No, because we want this on more than just text.

If not a class, then it should be a hook. I don't care either way, but I don't see anything wrong with classes tbh.

Copy link
Contributor

Choose a reason for hiding this comment

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

(please don't make this a blocker though, I'm fine either way)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@youknowriad The button has to be disabled/inert too, which is directly caused by the blurring/placeholder state, so imo these should go together

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe @youknowriad meant that we keep the split, but in the loading case, we render a disabled <Button> which has a <TextBlur> inside it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's what I understood yes, but imo blur and inert/hidden should always go together with blur. I don't mind either way I guess. I'll change it later

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because I will have to add inert/aria-hidden (same) to TextBlur, and then the implementor has to add it once more to the button. Tbh, I don't get at all what's wrong with having props instead of a component :)

@ellatrix ellatrix force-pushed the try/lazy-current-plan branch from 28fcad3 to 016436b Compare May 26, 2025 07:33
@ellatrix ellatrix force-pushed the try/lazy-current-plan branch from abdcc7a to f46c71b Compare May 26, 2025 13:07
@ellatrix ellatrix merged commit 31d8cdb into trunk May 27, 2025
11 checks passed
@ellatrix ellatrix deleted the try/lazy-current-plan branch May 27, 2025 13:16
@github-actions github-actions bot removed the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label May 27, 2025
@a8ci18n
Copy link

a8ci18n commented May 27, 2025

This Pull Request is now available for translation here: https://translate.wordpress.com/deliverables/17515948

Some locales (Hebrew) have been temporarily machine-translated due to translator availability. All other translations are usually ready within a few days. Untranslated and machine-translated strings will be sent for translation next Monday and are expected to be completed by the following Friday.

Thank you @ellatrix for including a screenshot in the description! This is really helpful for our translators.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants