You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every feature the hub lists is presented as a card: what the feature is, how much setting up it takes, which service it belongs to, and the controls to read more about it or set it up. One card serves every list the hub shows, wherever that list appears.
This issue builds that card, together with the one new UI primitive it needs — the setup effort indicator. The tabs that list the cards are built by their own issues (#13321, #13328), so what this issue delivers is the card itself, in Storybook.
The card's call to action (#13322), the detail panel its Read more control opens (#13330), and the menu behind its dismiss control (#13357) are also each their own issue. This issue provides the places they occupy.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
A feature card presents a feature registered in the catalog (Register the feature catalog with final copy #13247): its title, its short description, how much setup it takes, the service it belongs to — that service's icon and name — and its badges.
A feature that's Site Kit's own rather than a Google service's shows Site Kit's icon and the name "Site Kit feature".
How much setup a feature takes is shown by a new indicator: three wrench icons, of which one, two or three are filled to match the feature's setup effort, followed by that effort's label — "Just a few clicks", "A short setup" or "In depth setup".
The card shows the "New" badge while the feature is new to the user (Implement the newness model & derived selectors #13246), alongside whichever badges the feature itself carries, such as "Paid service". A list can turn the badge off: All services and features shows it, and What's new? doesn't, every feature it lists being new.
The card shows a red dot beside the feature's title while the feature is new to the user and they haven't yet seen it. A list can turn the dot off, as it can the badge: What's new? shows it, and All services and features doesn't.
When the feature is marked seen, the dot remains visible for three seconds, then briefly fades out.
The card's dismiss (X) control is optional, shown only where the list the card appears in allows features to be dismissed. What the control does is covered by Add the dismiss-with-feedback menu #13357.
The card and the effort indicator match the Figma designs, and are presented in Storybook.
Props: effort — the catalog entry's FEATURE_EFFORTS value.
Renders three wrench icons, the first effort of them carrying a --filled modifier and the rest muted, followed by that effort's label: 1 "Just a few clicks", 2 "A short setup", 3 "In depth setup". Keep the label map beside the FEATURE_EFFORTS constants in assets/js/googlesitekit/datastore/feature-discovery/constants.ts, so the detail panel's requirements sub-tab (Build the detail-panel sub-tab content (Description / Requirements / Screenshots) #13331) reuses it rather than repeating the copy.
The icons are decorative (aria-hidden); the label carries the meaning.
Add assets/js/components/feature-discovery/FeatureCard.tsx, modelled on assets/js/components/settings/SetupModule.js:
Props: slug, plus the three a list sets to say how it presents a feature — isDismissible, hideNewBadge and hideUnreadDot. Everything else is read from getFeature( slug ) and the per-user selectors.
Layout: a title row (unread dot, h3 title, badges, and the dismiss X at its trailing edge), the short description, EffortIndicator, then an action row with the feature's service on the left and the CTA slot followed by Read more on the right.
Service identity.ModuleIcon plus the module's name from core/modulesgetModule(), driven by an explicit moduleSlug on the catalog entry; an entry without one renders @/svg/graphics/logo-g.svg and the literal "Site Kit feature". It has to be explicit rather than derived from prerequisiteModules: Enhanced measurement, Visitor groups and Key Metrics all depend on analytics-4, but only Enhanced measurement is presented as an Analytics feature. The field is new, so the catalog entries registered in Register the feature catalog with final copy #13247 each need a value for it as well — read each one off the service named on that feature's card in the Figma frame.
Badges.NewBadge while isFeatureNew( slug ) and hideNewBadge isn't set — What's new? being the one list that sets it. Then one Badge per entry in the feature's badges[]; map the FEATURE_BADGES slugs to their labels (paid-service → "Paid service") alongside the constants.
Unread dot. An inlined span before the title in $c-red-r-400, shown while isFeatureUnread( slug ) and hideUnreadDot isn't set — All services and features being the one list that sets it.
The dot can't render straight off that selector, though: What's new? marks its whole list seen on view, which flips it false while the user is looking at the list. So hold the rendered value in state, and in a useEffect on the selector, start a three-second timeout when it flips false before clearing the state and fading the dot out — leaving the dot in place while the list is read, and absent on the next visit. Clear the timeout on unmount.
CTA slot. Render a placeholder in the action row, for FeatureCTA to replace.
Read more. A Button in the action row's trailing position; its onClick opens the detail panel, and isn't wired up here.
Dismiss X. An icon Button using @/svg/icons/close.svg with an aria-label naming the feature, rendered only when isDismissible.
Add assets/js/components/feature-discovery/FeatureListItem.tsx, a thin wrapper for FeatureCard.
Props: same as FeatureCard, passed through to its child.
This component will be expanded on later to render custom content for cards which need it.
Add assets/sass/components/feature-discovery/_googlesitekit-feature-card.scss and _googlesitekit-effort-indicator.scss, imported in assets/sass/admin.scss. Despite the name, a card isn't a boxed surface: it has no border, background or shadow of its own, and consecutive cards are told apart by a single horizontal rule between them. The card draws that rule as its own bottom border and suppresses it on :last-child, so the lists that use it need no separator styling of their own.
Storybook
Add a provideFeatures( registry, features ) helper to tests/js/utils.ts, following provideModules: each entry passed in is registered over a default one, so a story or test names only the fields it varies.
Co-locate FeatureCard.stories.tsx and EffortIndicator.stories.tsx, setting the registry up with WithRegistrySetup, provideFeatures and provideModules.
Card variants: a Google service's feature; a Site Kit feature; the feature as All services and features presents it (New badge, no dot); as What's new? presents it (dot, no New badge, dismissible); carrying a Paid service badge. Effort indicator: one story per effort.
Add a story rendering several cards in sequence, so the separating rules — and their absence below the last card — are visible and covered.
Give each story a scenario = {} so it picks up a VRT scenario.
Test Coverage
EffortIndicator.test.tsx — the filled icon count and the label for each effort.
FeatureCard.test.tsx — copy, effort and service resolved from the catalog entry; the "Site Kit feature" fallback for an entry with no moduleSlug; the New badge shown only while the feature is new and hideNewBadge isn't set, and the dot likewise under isFeatureUnread and hideUnreadDot; the dot still rendered for three seconds after the selector flips false, and gone once the timeout runs (jest.useFakeTimers()); the entry's own badges rendered; the X present only when isDismissible, and Read more always.
QA Brief
Storybook has a story for EffortIndicator which shows each effort label with the expected label and icons and matches the design.
Storybook has these stories for FeatureCard, each matching the designs and behaving reasonably across display sizes:
A standard Site Kit feature.
A feature belonging to a module, with a "Paid service" badge.
Two features with a separator between them.
A new feature, indicated by a "New" badge but no dot, as presented in the design under "All services and features".
A new feature, indicated by a dot but no "New" badge, with a dismiss button, as presented in the design under "What's new?".
The behaviour of the dot lingering for 3s is handled by FeatureCTA and covered by tests, but will need Build the "What's new?" tab #13321 for QA as the card needs to be marked as seen by something externally.
Changelog entry
Build the FeatureCard and EffortIndicator components for the upcoming Feature Discovery Hub, and present them in Storybook.
Feature Description
Every feature the hub lists is presented as a card: what the feature is, how much setting up it takes, which service it belongs to, and the controls to read more about it or set it up. One card serves every list the hub shows, wherever that list appears.
This issue builds that card, together with the one new UI primitive it needs — the setup effort indicator. The tabs that list the cards are built by their own issues (#13321, #13328), so what this issue delivers is the card itself, in Storybook.
The card's call to action (#13322), the detail panel its Read more control opens (#13330), and the menu behind its dismiss control (#13357) are also each their own issue. This issue provides the places they occupy.
For reference, see the UI component architecture section in the design doc, and the All services and features and What's new? designs in Figma.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
FeatureDetailPanel) #13330.FeatureCTAwith gating rules #13322.Implementation Brief
Storybook only — neither tab lists these cards yet.
assets/svg/icons/wrench.svg— a single glyph filled withcurrentColor, drawn three times by the effort indicator.assets/js/components/feature-discovery/EffortIndicator.tsx:effort— the catalog entry'sFEATURE_EFFORTSvalue.effortof them carrying a--filledmodifier and the rest muted, followed by that effort's label: 1 "Just a few clicks", 2 "A short setup", 3 "In depth setup". Keep the label map beside theFEATURE_EFFORTSconstants inassets/js/googlesitekit/datastore/feature-discovery/constants.ts, so the detail panel's requirements sub-tab (Build the detail-panel sub-tab content (Description / Requirements / Screenshots) #13331) reuses it rather than repeating the copy.aria-hidden); the label carries the meaning.assets/js/components/feature-discovery/FeatureCard.tsx, modelled onassets/js/components/settings/SetupModule.js:slug, plus the three a list sets to say how it presents a feature —isDismissible,hideNewBadgeandhideUnreadDot. Everything else is read fromgetFeature( slug )and the per-user selectors.h3title, badges, and the dismiss X at its trailing edge), the short description,EffortIndicator, then an action row with the feature's service on the left and the CTA slot followed by Read more on the right.ModuleIconplus the module's name fromcore/modulesgetModule(), driven by an explicitmoduleSlugon the catalog entry; an entry without one renders@/svg/graphics/logo-g.svgand the literal "Site Kit feature". It has to be explicit rather than derived fromprerequisiteModules: Enhanced measurement, Visitor groups and Key Metrics all depend onanalytics-4, but only Enhanced measurement is presented as an Analytics feature. The field is new, so the catalog entries registered in Register the feature catalog with final copy #13247 each need a value for it as well — read each one off the service named on that feature's card in the Figma frame.NewBadgewhileisFeatureNew( slug )andhideNewBadgeisn't set — What's new? being the one list that sets it. Then oneBadgeper entry in the feature'sbadges[]; map theFEATURE_BADGESslugs to their labels (paid-service→ "Paid service") alongside the constants.spanbefore the title in$c-red-r-400, shown whileisFeatureUnread( slug )andhideUnreadDotisn't set — All services and features being the one list that sets it.useEffecton the selector, start a three-second timeout when it flips false before clearing the state and fading the dot out — leaving the dot in place while the list is read, and absent on the next visit. Clear the timeout on unmount.FeatureCTAto replace.Buttonin the action row's trailing position; itsonClickopens the detail panel, and isn't wired up here.Buttonusing@/svg/icons/close.svgwith anaria-labelnaming the feature, rendered only whenisDismissible.assets/js/components/feature-discovery/FeatureListItem.tsx, a thin wrapper forFeatureCard.FeatureCard, passed through to its child.assets/sass/components/feature-discovery/_googlesitekit-feature-card.scssand_googlesitekit-effort-indicator.scss, imported inassets/sass/admin.scss. Despite the name, a card isn't a boxed surface: it has no border, background or shadow of its own, and consecutive cards are told apart by a single horizontal rule between them. The card draws that rule as its own bottom border and suppresses it on:last-child, so the lists that use it need no separator styling of their own.Storybook
provideFeatures( registry, features )helper totests/js/utils.ts, followingprovideModules: each entry passed in is registered over a default one, so a story or test names only the fields it varies.FeatureCard.stories.tsxandEffortIndicator.stories.tsx, setting the registry up withWithRegistrySetup,provideFeaturesandprovideModules.scenario = {}so it picks up a VRT scenario.Test Coverage
EffortIndicator.test.tsx— the filled icon count and the label for each effort.FeatureCard.test.tsx— copy, effort and service resolved from the catalog entry; the "Site Kit feature" fallback for an entry with nomoduleSlug; the New badge shown only while the feature is new andhideNewBadgeisn't set, and the dot likewise underisFeatureUnreadandhideUnreadDot; the dot still rendered for three seconds after the selector flips false, and gone once the timeout runs (jest.useFakeTimers()); the entry's own badges rendered; the X present only whenisDismissible, and Read more always.QA Brief
EffortIndicatorwhich shows each effort label with the expected label and icons and matches the design.FeatureCard, each matching the designs and behaving reasonably across display sizes:Note:
as a placeholder for BuildFeatureCTAwith gating rules #13322.FeatureDetailPanel) #13330 respectively.FeatureCTAand covered by tests, but will need Build the "What's new?" tab #13321 for QA as the card needs to be marked as seen by something externally.Changelog entry
FeatureCardandEffortIndicatorcomponents for the upcoming Feature Discovery Hub, and present them in Storybook.