components/user_education/feature-promos.md
Once you have the PRD spec for your promo, here are the steps you will follow to create the promo:
For historical reasons, and for compatibility with other non-desktop platforms,
Desktop User Education requires you to create your IPH feature under
/components/feature_engagement. This is required boilerplate for every promo.
The required changes are:
public/feature_constants.h
and .cc.
public/feature_list.h
and .cc.Required additions in /tools/metrics:
actions/actions.xmlhistograms/metadata/feature_engagement/histograms.xmlEnsure you are reproducing the name of your feature, as defined in
feature_constants.cc, any time you need to express your feature as a string. We have several tests that attempt to check for typos, but they're not perfect.
In addition to failing consistency check tests, neglecting to put your IPH in the histogram and action files will result in you missing out on critical telemetry and preclude you from using the User Education 2.0 metrics dashboards.
Historically, you would need to add a configuration to either
components/feature_engagement/public/feature_configurations.cc or your Finch
configuration, or both. However, Desktop User Education now auto-configures your
IPH for you, and will overwrite most configuration parameters.
You are strongly recommended not to add an entry for Desktop Chrome in this file! At best, it will be overwritten, and at worst it can break your IPH and possibly other IPH as well.
If you are planning on using the same IPH feature on iOS or Android, you can
(and should) put a configuration in this file, but surrounded by a build
directive, e.g. #if BUILDFLAG(IS_IOS). Note that even if you were to include a
desktop configuration, the values required to "play nice" with Desktop User
Education are different from those recommended on mobile, so you would still
need a separate mobile configuration.
All of these changes will go in /chrome/browser/ui.
Add your feature to MaybeRegisterChromeFeaturePromos() in
views/user_education/browser_user_education_service.cc.
There are many examples already present, which you can use as a model.
Useful things to know:
SetMetadata() to set accurate metadata.
HelpBubbleArrow::kNone will cause your help bubble to float
below the anchor element without a visible arrow. It is basically only used
for kTopContainerElementIdentifier, which is the View which holds the
tabstrip, toolbar, and horizontal bookmark bar.
More information on defining a new help bubble anchor is provided on the Help Bubbles page.
A "toast" IPH is a small blue bubble with no action buttons (other than a simple "x" close button). It does not take focus, and disappears after about ten seconds. Toast bubbles are used to draw attention to new or relocated UI elements and feature entry points.
When registering a toast, two localizable strings are required: the string to be displayed, and the string that will be read for screen reader users. These are intentionally different, and are required to be distinct. The reason for this is that most toasts have text like "Access [new feature] here", which is fine for someone who can see where the bubble is, but useless for low-vision users.
A good accessible string not only describes what is being promoted, but also how
to access it, e.g. "Access [new feature] using the [button description] button
on your toolbar". You can also describe the entry point in terms of an
accelerator; see FeaturePromoSpecification::CreateForToastPromo() for more
information.
It's fairly straightforward what Toast, Snooze, and Tutorial IPH do. However, a Custom Action allows for almost anything to happen when the user clicks the action button on the IPH. This is handled via a callback which is set when the promo is registered.
The most common things a custom action can do are:
In the latter case, we recommend but do not require using
ShowPromoInPage.
This opens the desired WebUI page and displays a help bubble on a specific
element (typically a specific setting). In order to use this, you will need to
assign an identifier to the anchor in the HTML; see the
Help Bubbles page for more info on help bubble anchors.
Please note that when you get the callback for the custom action, you will receive a
FeaturePromoHandle- this object must remain in scope/undestructed until you are done with your follow-up actions. Failure to do this may allow other promos to show over and interfere with those actions.
A keyed promo is an IPH that can be shown once per unique identifier, which is typically an App ID, GAIA ID, or some other similar string ID.
When showing a keyed promo you must pass in the key you wish to show it for,
in the FeaturePromoParams. A keyed promo is individually dismissed for each
unique key. Make sure when designing such a promo you have a good way of
uniquely identifying each use case via a unique string. You should never
generate unique strings on the fly - they should always be tied to some finite
set of elements associated with the user.
Keyed promos are higher-priority than normal promos and should be used for privacy/security-related IPH only. They are on an allowlist and must therefore be approved by the User Education team before they can be registered.
There are two ways to restrict which platforms an IPH shows on. One is via Finch
configuration; only enabling your IPH feature on the desired targets. The other
is only registering the IPH on supported platforms (or only on branded Chrome),
by using #if BUILDFLAG() compiler directives in
browser_user_education_service.cc.
Note that if you go the first route, you will get a "feature not enabled" status in the logs if you attempt to trigger the promo. This is expected, and can be filtered out by experiment in UMA.
If you go the second route and you try to trigger the IPH, you will get "error" messages in the histograms and logs, as it is a [non-fatal] error to try to trigger an unregistered promo. Therefore it is recommended to also gate the triggering logic in the same way, using the same compiler directives.
For anything that cannot be expressed with a
BUILDFLAG, it is recommended to always register the promo, and simply not show it if it wold not be appropriate. This creates more consistency in the system and makes it easier to diagnose problems when an expected IPH does not show.
To show your IPH call BrowserWindow::MaybeShowFeaturePromo(). This will
require having access to a BrowserWindow, BrowserView (which implements
BrowserWindow), or Browser object (which owns a BrowserWindow).
There are other calls you can make on BrowserWindow to e.g. gauge whether a
promo could show or determine whether a promo has been permanently dismissed.
Use them as needed but note that they are not zero-cost so try to avoid calling
them in a tight loop.
When your feature's entry point is used, it is considered good form to note this
so that you don't continue to promote the feature via IPH. You can do this by
calling BrowserWindow::NotifyPromoFeatureUsed() or - if you only have access
to the profile and not the browser window at the call site -
UserEducationService::MaybeNotifyPromoFeatureUsed(). The feature you should
pass in is the IPH feature.
MaybeShowFeaturePromo() lets you pass either just a feature or a
FeaturePromoParams object. The latter allows for substituting text in the
body, title, or screenreader strings. There are several options, including a
single string, multiple strings, and singular/plural.
In order to use these substitutions, you will need to have matching
substitution fields in the localizable strings defined in your .grd[p] files.
Because of the sheer number of promotions that feature teams put into Chrome, and the potential for significant disruption for users, great care has been taken to limit the number and frequency of certain promotions.
We are enumerating these restrictions here because there are no exceptions. If triggering logic causes an IPH to run afoul of one or more of these restrictions and the IPH is not shown (or is shown less than would be desired), it is up to the feature developer to rework the triggering logic and/or to adjust their expectations.
Restrictions placed on all IPH:
Restrictions on "heavyweight" IPH (Snooze, Tutorial, Custom Action) - note that these only apply to normal-priority promos:
Future planned restrictions on all IPH:
You should ideally only call MaybeShowFeaturePromo() in response to specific
user actions, UI changes, or events in the system.
For example, when the user opens the Nth tab and has never created a tab group, show the Tab Groups IPH. When the autofill dialog pops up, show the "we can now suggest autofill for unlabeled fields" IPH. When free memory reaches some minimum threshold, show the Memory Saver IPH.
"Toast" promos can also be shown immediately to indicate that some UI element has moved. For example, it is okay to show a "your side panels can now be found here" IPH at startup if the user was regularly using the side panel. (Certain legal notices are also required to show at startup.)
Avoid calling MaybeShowFeaturePromo() over and over, or preemptively before
the user has taken an action that suggests they would benefit from the feature
being promoted. If you want to promote a new feature at startup, consider
instead putting it on the "What's New" Page, or using a "New" Badge, or both.
See Getting Started for links to these other options.
If your promo is eligible to show at startup (see section above) then you can
use BrowserWindow::MaybeShowStartupFeaturePromo(), which you should call ASAP
during startup. Your promo will be displayed when ready, unless preempted by
higher-priority messaging, and you can opt to get a callback with the result.
This cannot effectively be used with heavyweight, normal-priority IPH, which should never show at startup.
There are three ways to test a promo manually, and one preferred way to write regression tests for your promo.
Run chrome with a fresh profile (--user-data-dir=...) and (if your IPH is
heavyweight and normal-priority) with User Education rate-limiting disabled
(--disable-user-education-rate-limiting). Perform the steps that should
trigger your IPH. Verify it shows when it should, and then verify that it does
not reshow when dismissed or after your feature is used.
If you want to see or clear the data around your IPH feature (including whether
it has been permanently dismissed) go to chrome://user-education-internals,
find your feature, and expand the trays underneath to see and optionally clear
the recorded data. Clearing the data can allow you to trigger your feature over
and over on the same profile.
Demo mode is an older mode that bypasses most checks for whether a promo can
be displayed. Go to chrome://flags/#in-product-help-demo-mode-choice and
select your promo, then restart Chrome. This will eliminate any restriction on
reshowing your promo, so you may trigger it any number of times. Using your
feature's entry point will have no effect on the promo.
This approach is not preferred because it sidesteps logic you might want to test. But it is useful if all you want to do is ensure that your triggering logic is getting called in the right place.
If you just want to preview what your help bubble will look like, you can go to
the tester page at chrome://user-education-internals, find your promo, and
click the "Launch" button. This will attempt to show your IPH in the current UI.
Note that if your IPH anchors to an element that is not present, it will not be
able to show.
This approach is useful when you want to verify the appearance of your help bubble, how the text, title, image, and buttons will look, etc. However it is of very limited application and does not verify that your IPH will actually show when you want it to.
To test your IPH, use InteractiveFeaturePromoTest as your test's base class.
If you already have a test class for your feature you want to inherit from that
derives from InProcessBrowserTest, use
InteractiveFeaturePromoTestMixin<YourTestClass> instead.
Then, write a Kombucha test which
performs the steps that would trigger your IPH, with a WaitForPromo to verify
your promo is shown.
Interactive tests should almost always be in interactive_ui_tests, not
browser_tests. An InteractiveFeaturePromoTest can be in browser_tests only
so long as nothing in the tests would break if the browser window or any piece
of secondary UI (such as a dialog or menu) were to randomly lose focus.
browser_tests does not guarantee that the browser is the only foreground
program on the test machine, and many dialogs and menus in Chrome disappear on
loss of focus.
You need only enable your IPH feature (declared in feature_constants.h) in
your Finch configuration and in fieldtrial_testing_config.json, as with any
other feature.
You can roll the IPH feature out alongside the feature it is promoting, or you can A/B test the IPH within the arm of your study where the promoted feature is rolling out to gauge the success of your User Education campaign.
In general, it is safe to just roll them out together. Also, try to avoid having a situation where a user has the IPH but not the promoted feature; this usually won't cause errors, but will result in a lot of pollution in the UMA dashboards.
For IPH that are nudging users to do something more efficiently with an existing (i.e. fully rolled out to stable) feature, the same guidelines apply, except that there is no promoted feature to roll out; you will still need to do a normal rollout of the promo feature.