Skip to content

Build the wp-admin menu count badge + its global count bundle #13359

Description

@techanvil

Feature Description

WordPress has a long-established way of telling an admin there's something waiting for them: a small count bubble on the menu item, as plugin updates and pending comments use. The hub's Add Features menu item (#13244) uses the same convention — a count of the features that are new to this user — and, crucially, shows it wherever the user happens to be in wp-admin, not only on Site Kit's own pages. That's the point of it: it reaches a user who isn't looking at Site Kit at all.

What makes this more than a menu tweak is where the number comes from. What counts as new is per user, and whether a feature is already set up is a live check against the user's services (#13246, #13247), so the count can only be worked out where Site Kit's data layer is running. So Site Kit works it out on the pages where it already runs — the hub, both dashboards, and the Site Kit widget on the WordPress dashboard, which is the page WordPress lands an admin on when they sign in — remembers it in the browser, and a very small script on every admin screen renders what was remembered. One place decides the number, one place draws it, and no rule about newness is duplicated anywhere.

The trade-off is deliberate: the count is as fresh as the last time the user was on a Site Kit page. When something has happened that could have changed it — Site Kit updating and bringing new features with it, or another administrator connecting a service — the remembered number can no longer be trusted, and the menu item shows a plain dot with no number until it's worked out again. A dot still says there's something new here, without asserting a number that might be wrong.

For reference, see The global count bundle and Entry points & new-feature indicators in the design doc, and the menu badge in Figma.


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

Acceptance criteria

  • The Add Features item in the Site Kit menu (Stand up the hub page: Screen, entrypoint & view context #13244) carries a count bubble showing how many features are new to this user and not yet seen (Implement the newness model & derived selectors #13246) — the same number behind the dashboard header pill's dot (Build the dashboard header pill (AddFeaturesButton) #13358).
  • The bubble uses WordPress's own menu count styling, as plugin updates and pending comments do, and its meaning is available to screen readers.
  • The bubble is shown on every wp-admin screen, not only Site Kit's own pages.
  • When the count is zero, no bubble is shown.
  • The count is worked out and remembered whenever the user loads a page where Site Kit derives it: the hub, the main dashboard, the entity dashboard, and the Site Kit widget on the WordPress dashboard.
  • The remembered count is used only while it still matches the plugin version and the site's service connection state it was worked out against. If either has changed since — Site Kit was updated, or another administrator connected a service — the item shows a plain dot with no number, until the count is next worked out on one of those pages.
  • Visiting What's new? (Build the "What's new?" tab #13321) clears the count, and any other wp-admin tabs the user has open update to match without needing to be reloaded.
  • Logging out clears the remembered count, so the next person to use that browser doesn't see it.
  • The count is remembered per browser, so a user signing in on a second browser sees no bubble there until they load a Site Kit page.
  • The badge is loaded only for administrators, and only when the featureDiscoveryHub feature flag is enabled — so only users who have an Add Features item to badge carry any of this.
  • What's added to every admin screen is deliberately minimal: it reads the remembered count and renders the bubble. It loads no Site Kit data stores, no React, and makes no API requests.
  • If the browser can't store the count, no bubble is shown and nothing else is affected.

Implementation Brief

  • In includes/Core/Admin/Screens.php:
    • For the googlesitekit-features screen, add the following to the menu_title:
      <span class="menu-counter googlesitekit-features-badge count-0">
        <span aria-hidden="true" class="count">span>
        <span class="screen-reader-text">span>
      span>
    • Use __( 'Add Features %s', 'google-site-kit' ) and sprintf() for the appending, for consistency with core's example.
  • Create assets/js/util/features-badge.ts:
    • Define const FEATURE_COUNT_CACHE_KEY – googlesitekit::feature-count.
    • Define const FEATURE_COUNT_CHANNEL_NAME – googlesitekit::feature-count.
    • Define const FEATURE_COUNT_CHANNEL_MESSAGES.UPDATED – 'updated'
    • Define interface FeatureCountFingerprint – { connectedModules: string[], pluginVersion: string, userID: number }.
    • Define interface FeatureCountCache – extends FeatureCountFingerprint { count: number }.
    • Export function isFeatureCountCache( value: unknown ): value is FeatureCountCache:
      • Check value is an object with properties consistent with a FeatureCountCache.
      • value is FeatureCountCache is a type predicate, which helps TypeScript recognise that any value that passes the check is a FeatureCountCache.
    • Define function getFeatureCountCache(): FeatureCountCache | null:
      • Get cache from local storage with FEATURE_COUNT_CACHE_KEY.
      • Parse JSON and check isFeatureCountCache().
      • Return result or null on parsing error or check failure.
    • Export function clearFeatureCountCache():
      • Clear cache from local storage with FEATURE_COUNT_CACHE_KEY.
      • Post FEATURE_COUNT_CHANNEL_MESSAGES.UPDATED as a message on a BroadcastChannel for FEATURE_COUNT_CHANNEL_NAME.
    • Export function setFeatureCountCache( featureCountCache: FeatureCountCache ):
      • Check isFeatureCountCache() and stringify.
      • Return on error or check failure.
      • Set cache in local storage with FEATURE_COUNT_CACHE_KEY.
      • Post FEATURE_COUNT_CHANNEL_MESSAGES.UPDATED as a message on FEATURE_COUNT_CHANNEL_NAME.
    • Export function renderFeaturesBadge( count: number, showCount: boolean ):
      • Find .googlesitekit-features-badge element.
      • Return if it doesn't exist.
      • Set text content of the .count element to count if showCount, otherwise an empty string.
      • Replace the existing count-* class with count-${count}.
      • Set text content of the screen-reader-text element with:
        • If showCount – _n( '%d new feature', '%d new features', count, 'google-site-kit' )
        • Otherwise __( 'new features', 'google-site-kit' ).
    • Export function renderFeaturesBadgeFromCache( fingerprint: FeatureCountFingerprint ):
      • Get the cache with getFeatureCountCache().
      • If one is found:
        • Call clearFeatureCountCache() and return if fingerprint.userID doesn't match the cache's userID.
        • Call renderFeaturesBadge() with the cache count, and pass showCount as true if the fingerprint version and connected modules match the cache's.
      • Otherwise call renderFeaturesBadge( 0, false ).
    • Make sure to try/catch with localStorage and BroadcastChannel, failing silently.
  • Create assets/js/components/feature-discovery/useFeatureCountCache.ts:
    • Export hook useFeatureCountCache():
      • Keep track of whether getModules has finished resolution.
      • Get connectedModules by selecting CORE_MODULES.getModules() and using CORE_MODULES.isModuleConnected().
      • Get count from CORE_FEATURE_DISCOVERY.getNewFeatureCount().
      • Get pluginVersion from global.GOOGLESITEKIT_VERSION.
      • Get userID from CORE_USER.getID().
      • Call setFeatureCountCache() with those values in an effect if selectors are resolved and count is not undefined.
  • In each of the following components, import and call useFeatureCountCache():
    • assets/js/components/DashboardMainApp.js
    • assets/js/components/DashboardEntityApp.js
    • assets/js/components/feature-discovery/FeatureDiscoveryApp.tsx
    • assets/js/components/wp-dashboard/WPDashboardApp.js
  • Create assets/js/googlesitekit-features-badge.ts:
    • Import clearFeatureCountCache, FEATURE_COUNT_CHANNEL_NAME, renderFeaturesBadgeFromCache.
    • Get connectedModules, pluginVersion, resetSession, userID from _googlesitekitFeaturesBadgeData.
    • Define featureCountChannel – new BroadcastChannel( FEATURE_COUNT_CHANNEL_NAME ).
    • Define function onMessage( event: MessageEvent ):
      • If the message is FEATURE_COUNT_CHANNEL_MESSAGES.UPDATED, call renderFeaturesBadgeFromCache({ connectedModules, pluginVersion, userID }).
    • Define function setupFeaturesBadge():
      • Bind onMessage to the message event on a BroadcastChannel for FEATURE_COUNT_CHANNEL_NAME.
      • Call clearFeatureCountCache() if resetSession.
      • Call renderFeaturesBadgeFromCache({ connectedModules, pluginVersion, userID }).
    • Call setupFeaturesBadge().
  • In includes/Core/Modules/Modules.php:
    • In register() add a filter to googlesitekit_connected_modules that returns $this->get_connected_modules().
  • In includes/Core/Assets/Assets.php:
    • Add a get_inline_features_badge_data() method returning:
      • connectedModules – Slugs of modules from Modules::get_connected_modules() by applying the googlesitekit_connected_modules filter added above to an empty array.
      • pluginVersion – GOOGLESITEKIT_VERSION.
      • resetSession – Whether the googlesitekit_reset_session URL parameter is true.
      • userID – get_current_user_id().
    • In get_assets(), if the user has Permissions::MANAGE_OPTIONS and featureDiscoveryHub is enabled:
      • Add googlesitekit-features-badge-data as a Script_Data:
        • global – _googlesitekitFeaturesBadgeData.
        • data_callback – Return get_inline_features_badge_data().
      • Add assets/js/googlesitekit-features-badge.ts as a Script:
        • dependencies – googlesitekit-i18n and googlesitekit-features-badge-data.
        • load_contexts – Asset::CONTEXT_ADMIN_GLOBAL.
    • In register():
      • Add an action to admin_enqueue_scripts that enqueues scripts with Asset::CONTEXT_ADMIN_GLOBAL, like the existing enqueue_block_assets and enqueue_block_editor_assets examples.
  • In assets/webpack/basicModules.config.js
    • Add an entry for googlesitekit-features-badge.
  • In assets/js/types/globals.d.ts
    • Type _googlesitekitFeaturesBadgeData.

Test Coverage

  • Add unit test coverage for:
    • features-badge.ts, checking cache validation, rendering results and fingerprint comparisons.
    • useFeatureCountCache.ts, checking the cache updates when expected.
    • googlesitekit-features-badge.ts, checking event handling.
  • Update unit test coverage for Core\Assets to check get_inline_features_badge_data() returns the expected data.

QA Brief

  • Set up a new Site Kit site and enable the featureDiscoveryHub feature flag.
  • Connect a second admin account to Site Kit. Log in as the original account before proceeding.
  • In the Site Kit admin sub-menu an Add Features item should be visible, without any count or badge.
  • With the tester plugin set Force initial Site Kit plugin version to 1.84.0.
  • Navigate to Pages > All Pages. Hover over the Site Kit menu item, no badge should appear.
  • Navigate to Site Kit > Dashboard. A "5" badge should appear next to _Add Features.
  • Navigate back to Pages > All Pages. The "5" badge should persist.
  • Keep that page open and in an incognito window, or another browser, log in and connect Analytics. Once Analytics is fully connected the badge should read "8".
  • Return to the original browser and refresh. Hover over the Site Kit admin menu item. In the sub menu a dot should be shown next to Add Features with no number.
  • Navigate to Posts > All Posts. The numberless dot should persist.
  • Navigate to Dashboard > Home, the WordPress dashboard. The number should update to "8".
  • Open a new a tab in the same window. In that window disconnect Analytics. The badge should become a numberless dot. Navigate to Site Kit > Dashboard and it should return to "5".
  • Return to the original tab and hover over the Site Kit admin menu. The badge should have changed to a numberless dot. Refresh and it should update to "5".
  • Navigate to Site Kit > Dashboard, open a new tab and re-connect Analytics, when returning to the dashboard the number should update to "8". Return to the original tab and the number should also have updated to "8".
  • Log out of WordPress and attempt to visit /wp-admin/edit.php (to ensure a login redirect to a non-Site Kit page). Log in and hover over the Site Kit admin menu and the badge should read "8".
  • Log out and and attempt to visit /wp-admin/edit.php again, but log in as the second admin user. The badge should not appear.
  • Navigate to Site Kit > Dashboard and the number should update to "8".
  • Open a second tab to Posts > All Posts. Return to the tab on the Site Kit dashboard.
  • In the browser console, run googlesitekit.data.dispatch( 'core/feature-discovery' ).markFeaturesSeen( [ 'email-reports' ] );. The number should update to "7" in both tabs.
  • Log in as the original admin and return to the Site Kit dashboard. The number should be "8".

Changelog entry

  • Show the new feature count in the "Add Features" admin menu item.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0High priorityTeam MIssues for Squad 2Type: EnhancementImprovement of an existing feature

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions