这是indexloc提供的服务,不要输入任何密码
Skip to content

Combine similar CTA components returned from widgets #2252

@felixarntz

Description

@felixarntz

Widgets may return certain predefined components to indicate certain states:

  • ReportError
  • ReportZero
  • ActivateModuleCTA
  • CompleteModuleActivationCTA

The last three of those are module-specific and will result in the same UI for each instance they are displayed in (for the same module). If multiple widgets next to each other return the same of those components (i.e. same component with same props), such components should be aggregated and only displayed once. For example, if the Analytics module is not active, only a single ActivateModuleCTA for Analytics should be displayed in the "Search Funnel" widget area instead of two, even though it results from two different widgets.


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

Acceptance criteria

  • Multiple instances of ReportZero, ActivateModuleCTA, and CompleteModuleActivationCTA returned by adjacent widgets in the same row should be combined per the module that they apply to.
    • The widget widths of widgets combined should be combined. For example, three quarter widgets returning the same component for the same module should result in a single three-quarter width version of that component being rendered.
    • For example, if two quarter adjacent widgets return ReportZero for Search Console and the next two quarter widgets return ActivateModuleCTA for Analytics, they should be combined to be one half ReportZero for Search Console and one half ActivateModuleCTA for Analytics.
    • Components should not be combined if they would span across multiple rows. For example, if in the above scenario there was another two quarter widgets returning ActivateModuleCTA for Analytics, they should be combined with each other, but not with the previous combination of two ActivateModuleCTA for Analytics because this would mean they would be combined across rows, which would mess up the UI.
  • Widgets should not have to set state for this to work. They should only have to return the specific comments, and those components should internally support handling the necessary details (e.g. via state and useEffect or similar).
  • There should be Storybook coverage for widgets where some of them return certain special state components, some in ways that would be combined etc., to test the behavior. The following stories in particular should be included:
    1. 4 quarter widgets: Normal, ReportZeroData Search Console, ReportZeroData Analytics, ActivateModuleCTA AdSense
    2. 4 quarter widgets: ReportZeroData Search Console, ReportZeroData Search Console, ReportZeroData Analytics, ReportZeroData Analytics
    3. 4 half widgets: ReportZeroData Search Console, ActivateModuleCTA Analytics, ActivateModuleCTA Analytics, ActivateModuleCTA AdSense
    4. 2 half widgets CompleteModuleActivationCTA Search Console, 1 quarter widget CompleteModuleActivationCTA Search Console, 1 quarter widget CompleteModuleActivationCTA Analytics, 1 quarter widget "normal", 1 quarter widget CompleteModuleActivationCTA Analytics

Implementation Brief

  • The core/widgets datastore should receive a new action:
    • setWidgetState( slug, Component, metadata = {} ) receives the WPComponent and props that are relevant to indicate "commonalities" (i.e. widgets with the same Component and metadata would be combined).
    • unsetWidgetState( slug, Component, metadata = {} ) receives the same parameters as setWidgetState and will unset the corresponding widget state.
  • The core/widgets datastore should receive a new selector:
    • getWidgetState( slug ) returns an object with Component and metadata properties from above.
  • New components WidgetReportZero, WidgetActivateModuleCTA, and WidgetCompleteModuleActivationCTA should be introduced:
    • They should require a widgetSlug prop.
    • They should require a moduleSlug prop and wrap the respective component without the Widget* prefix. They should also pass on any other props to the child component.
    • They should rely on useEffect to call setWidgetState with a cleanup callback to call unsetWidgetState.
    • The existing ActivateModuleCTA and CompleteModuleActivationCTA components should be modified to have their slug prop renamed to moduleSlug so that the signature matches exactly their Widget* variants (minus the widgetSlug prop). For ReportZero, the prop is already called moduleSlug.
  • Every widget should receive props WidgetReportZero, WidgetActivateModuleCTA, and WidgetCompleteModuleActivationCTA, which are wrapped/scoped versions of the general components mentioned above - scoped as in that the widgetSlug is already internally passed there, so that the widget component that may render them doesn't manually need to pass the slug, which would be error-prone.
  • Remove AnalyticsInactiveCTA and rely on ActivateModuleCTA instead, allowing widgets to consistently use the latter component and without requiring us to implement a WidgetAnalyticsInactiveCTA which would be overly specific. For now, let's just move the alternative title and description into the ActivateModuleCTA component to still have them centralized. This should be revisited and improved - let's just add a TODO for now, as we shouldn't update the canonical module description etc in this issue.
  • Modify the existing getWidgetClassNames() into a getWidgetLayout() that does not only return the resulting class names for each widget, but also the final column width for each widget and the row index for each widget (while there's only a single Row component in a widget area, widgets may extend beyond the visual row and thus be spread across multiple rows).
  • Introduce a new combineWidgets() function that receives the active widgets, their current widget states, plus the class names, column widths and row indexes from getWidgetLayout(). This function will take care of detecting whether consecutive widgets in the same row have a similar state (i.e. same Component and metadata). If so, adjust class names and specify override components (see below) as necessary.
  • Update WidgetRenderer so that it can receive an optional OverrideComponent prop. If it is passed, render that one instead of the registered widget component. Technically, still render the actual widget component too but hide it via CSS. This is necessary for the hooks to work.
  • Update WidgetAreaRenderer to use getWidgetLayout() and then combineWidgets(). Then pass the return values of the latter (i.e. the final CSS class names and the potential override components) to WidgetRenderer.
  • Add the Storybook stories from the ACs under Global/Widgets.
  • Merge Combine similar widget CTAs #2538.

Test Coverage

  • Add Jest tests for the new getWidgetLayout and combineWidgets functions.
  • Add Jest tests for the new Widget*-prefixed components.

Visual Regression Changes

  • N/A

QA Brief

Regular

  1. Use a development build.
  2. Set up Site Kit without any extra module activated.
  3. Ensure that the CTA to set up Analytics and the CTA to set up PageSpeed Insights look exactly the same for the new widgets API-powered part of the dashboard as it does for the legacy part of the dashboard.

Engineering

  1. Check the Global/Widgets/Widget Area\Special combination states story in Storybook.
  2. Ensure that all 4 stories from the ACs are implemented and that the widgets within them are combined as expected:
    • Adjacent widgets of the same special type (ReportZero, ActivateModuleCTA, CompleteModuleActivationCTA) must be combined as long as they are for the same module.
    • Any blue box must span the grid column width that all the widgets that it is resulting from would have spanned, had they been rendered regularly (i.e. without a special state).

Changelog entry

  • Implement logic to automatically combine UI for widgets that are in special states, e.g. widgets without sufficient API response data, or widgets that require a specific module to be set up first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Medium priorityRolloverIssues which role over to the next sprintType: EnhancementImprovement of an existing feature

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions