google / site-kit-wp

Site Kit is a one-stop solution for WordPress users to use everything Google has to offer to make them successful on the web.
https://sitekit.withgoogle.com
Apache License 2.0
1.22k stars 277 forks source link

Decouple module-specific effects from core components #8211

Open aaemnnosttv opened 5 months ago

aaemnnosttv commented 5 months ago

Feature Description

As a general principal, module-specific code can depend on core infrastructure, but not the other way around. Core infra being core data stores as one of the most explicit examples, but also things like very top-level components like DashboardMainApp.

The SK dashboards have a well-defined API for adding elements to the dashboard but the concept of widgets is directly associated with layout. Sometimes we want to invoke various module-specific side-effects which in the past have either been shoe-horned into an existing component, or added into a higher level component where they don't really belong.

There have been a few instances recently where we've wanted to use such functionality where it hasn't been available such as synchronizing module settings and creating custom dimensions to name a few.

One solution to this is to use the Widgets API – even today this can be used to accomplish the end goal.

Example

widgets.registerWidget(
    'analyticsDashboardEffects',
    {
        Component( { WidgetNull } ) {
            useEffect( () => {
                console.log( 'analyticsDashboardEffects:effect-run' );

                return () => console.log( 'analyticsDashboardEffects:effect-cleanup' );
            }, [] );

            return <WidgetNull />;
        },
        width: widgets.WIDGET_WIDTHS.FULL, // Required but irrelevant.
        priority: 0,
        modules: [ 'analytics-4' ],
    },
    [
        AREA_MAIN_DASHBOARD_TRAFFIC_PRIMARY,
    ]
);

This registers a widget on the main dashboard for the sole purpose of running effects. This will work, and by using WidgetNull unconditionally, the layout will never be affected by the "empty" widget. This could be combined with the whenActive() HOC for guarding it to only run when a module is active or connected. It isn't entirely ideal as-is here as there are things like width that aren't relevant, nor should we need to render WidgetNull to avoid throwing a wrench in the layout when it isn't concerned with layout at all.

One idea is to register a new widget area that would always be hidden which could be used for this kind of thing, and maybe we build a bit of a layer on top to avoid needing to define properties that aren't relevant for this kind of widget/area. Most of the parts of the Widget API are still relevant though like declaring modules for use with Dashboard Sharing, so it doesn't seem that a new API of sorts would be warranted.


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

Note: the example in the description is not a requirement but could be used as an part of an initial implementation. It could be worth exploring what a proper API for this would look like, although there would likely be substantial overlap with features already provided by the widgets API.

Implementation Brief

Test Coverage

QA Brief

Changelog entry

jamesozzie commented 3 months ago

One more report of this error in the support forums. Awaiting user feedback.

adamdunnage commented 3 weeks ago

One more report of this to add here.

User has shared their SH Info and confirmed that they don't encounter the same from an Incognito window. I will continue to troubleshoot this with the user and add any more updates here if I get them.

eugene-manuilov commented 3 weeks ago

Thanks, @zutigrm, but please do not use the PR as IB approach because it is strongly discouraged. If you can't write an IB without coding it beforehand, then just leave it for someone else. By spending time on your PoC, you spend time on implementation in the current sprint for a thing that is supposed to be implemented in a following sprint, thus our planning for that sprint will less accurate. Plus it is not always the case that PoC will be needed, thus time spent on it will be wasted.

Add assets/js/googlesitekit/widgets/datastore/effects.js

  • Add registerModuleEffect action, which should be a minimal adaptation from registerWidget action, only handling slug and effect - callback hook that will be passed

This is a nice start, but I don't think it should be done this way. The problem here is that you try to squeeze module related functionality into the widgets domain that should know nothing about that. We need to separate concerns.

The more appropriate solution would be updating the registerModule function to accept two new properties, let's say MainRootComponent and EntityRootComponent (similarly to SettingsEditComponent, SettingsViewComponent, etc). These two components will be responsible for all side effects that a module needs to do and will be rendered on MainDashboard and EntityDashboard respectively.

Using components approach will let us perform side effects that rely on certain criteria to happen and can be deferred in time.

  • syncGoogleTagSettings from DashboardEntryPoint into a separate hook

Looks like there is no syncGoogleTagSettings in the DashboardEntryPoint component anymore.

zutigrm commented 3 weeks ago

@eugene-manuilov Thanks for the feedback/clarification and new route suggestion. I updated IB to reflect that approach.

eugene-manuilov commented 3 weeks ago

Thanks, @zutigrm. Mostly looks good.

Add assets/js/googlesitekit/modules/ModuleEffects.js

Let's put it into the assets/js/components folder and call it ModuleRootComponents.

Update assets/js/components/DashboardMainApp.js

We need to update the DashboardEntryPoint component to render ModuleRootComponents. It should render the root components element + either ModuleSetup or DashboardMainApp depending on the setupModuleSlug value.

Extract:

  • Custom dimensions logic from DashboardMainApp into a separate component assets/js/modules/analytics-4/dashboard/SideEffects.js, which only contain hooks, and return null in the renderer
zutigrm commented 2 weeks ago

@eugene-manuilov Thanks, IB updated

eugene-manuilov commented 2 weeks ago

Thanks, @zutigrm. IB ✔️

aaemnnosttv commented 1 week ago

This is good to go now but we should only merge it after the split for the release. Assigning to @tofumatt to do that afterwards 👍

jamesozzie commented 19 hours ago

Just a note to mention there are a couple more reports of this users impacted by this over the past few days. Details below:

  1. https://wordpress.org/support/topic/sitekit-encountered-an-error-not-working/
  2. https://wordpress.org/support/topic/site-kit-encountered-an-error-131/