components/user_education/custom-help-bubbles.md
A custom help bubble is a UI used specifically for feature promos instead of a normal blue help bubble. You can use a custom help bubble when you want all of the normal rate-limiting and contention management of the feature promo system, but you need something other than a blue bubble.
Note that custom help bubble UI are subject to allowlisting and UI review; custom help bubble UI must conform to the normal requirements of help bubbles:
Note that custom help bubble UI will be focused when shown, so take that into account when designing them, especially for screen reader users.
When registering browser_user_education_service.cc use
FeaturePromoSpecification::CreateForCustomUi(). You'll note that this takes a
CustomHelpBubbleFactoryCallback which returns a CustomHelpBubble. Most of
the time you won't be directly creating your own CustomHelpBubble (though you
can!) but instead using a utility method to directly wrap your UI.
Which utility method you use to create your CustomHelpBubbleFactoryCallback
will depend on whether you are creating a Views BubbleDialog or a WebUI dialog.
If you are not creating one of these supported UI types, you will have to both
create your own UI and your own CustomHelpBubble class to wrap it.
The simplest way to create a custom UI for your promo is by making your own bubble dialog.
Here are the steps:
BubbleDialogDelegateView and also inherit from
CustomHelpBubbleUi.CustomHelpBubbleUi::NotifyUserAction() with one of the appropriate values
(depending on the type of action the user took).
NotifyUserAction() then an
"aborted" result will be registered instead of the correct result.DialogDelegate::SetCancelCallback() and
DialogDelegate::SetAcceptCallback() to set up some default behavior
handling.That's it! There's a ready (if very simple) example in TestCustomHelpBubbleView. For layout examples (including how to get the close button positioned) check out HelpBubbleView.
To register a Custom UI feature promo with a Views-based custom bubble, first
follow all of the normal steps for feature promos, then
bind a CustomHelpBubbleViewFactoryCallback and pass it to
CreateCustomHelpBubbleViewFactoryCallback().
Example:
// This could go inline in the call below, but is separated to make the
// example clearer.
auto my_custom_help_bubble_view_factory_callback =
base::BindRepeating(
[](ui::ElementContext from_context,
HelpBubbleArrow arrow,
FeaturePromoSpecification::BuildHelpBubbleParams build_params) {
return std::make_unique<MyCustomUiBubbleDialogView>(
build_params.anchor_element->AsA<views::TrackedElementViews>()->view(),
arrow);
});
feature_promo_registry.Register(
user_education::FeaturePromoSpecification::CreateForCustomUi(
kIPHMyCustomUIPromoFeature,
kAnchorElementId,
CreateCustomHelpBubbleViewFactoryCallback(
my_custom_help_bubble_view_factory_callback));
)
Note: you can use the from_context parameter to locate the browser that the
promo is being launched from, even if build_params.anchor_element is in a
different window.
This example assumes that the anchor is a View, however you can get creative
if you want to; see
FloatingWebUIHelpBubbleFactory
for one of the more sophisticated examples.
For handling of "custom action" IPH-like dialogs, see below.
You can create dialogs without the limitations of Views by using WebUI (though there is significantly more boilerplate involved). When you create a WebUI in this way, it must be a "Top Chrome" WebUI, which has a few extra requirements so it can be easily wrapped in a bubble dialog.
On the browser/C++ side:
TopChromeWebUIController and
CustomWebUIHelpBubbleController.
enable_chrome_send = true as well.UserEducation. histogram bucket for
Top Chrome WebUI performance metrics.webui::SetupWebUIDataSource() in the constructor with the appropriate
parameters.WEB_UI_CONTROLLER_TYPE_DECL() in the header and
WEB_UI_CONTROLLER_TYPE_IMPL(ClassName) in the source file.DECLARE_TOP_CHROME_WEBUI_CONFIG(ClassName) to automatically create a
config for your WebUI..AddWebUIConfig(ClassNameConfig) in
this file.RegisterWebUIControllerInterfaceBinder() in
this file.
CustomHelpBubbleHandlerFactory and any additional
mojo bindings you need.On the WebUI/Typescript side:
CustomHelpBubbleHandlerInterface by calling
CustomHelpBubbleProxyImpl.getInstance().getHandler().handler.notifyUserAction() with the appropriate value.
Unlike Views, CustomWebUIHelpBubbleController is already a convenience class
that handles a lot of the boilerplate (yes, there's more than what's listed
above).
To register a Custom UI feature promo with a WebUI-based custom bubble, first
follow all of the normal steps for feature promos, then
pass your WebUI's URL to MakeCustomWebUIHelpBubbleFactoryCallback<T>().
Example:
feature_promo_registry.Register(
user_education::FeaturePromoSpecification::CreateForCustomUi(
kIPHMyCustomUIPromoFeature,
kAnchorElementId,
MakeCustomWebUIHelpBubbleFactoryCallback<
MyCustomHelpBubbleWebUIController>(kMyWebUIUrl)));
For handling of "custom action" IPH-like dialogs, see below.
There are three ways you can add additional behavior for after your bubble closes:
An example of this would be the user clicking on a link in a WebUI dialog that opens, say, a settings page.
Alternatively, a button might call some function directly (over a different mojo interface if in a WebUI) that performs some action in the browser outside the help bubble.
In all these cases, the bubble code should send UserAction::kAction, which
will cause the bubble to close and record the correct IPH result. No further
action is needed.
An example of this would be the bubble having a "Show Me" button that you want to have open settings, start a tutorial, or something else.
In this case, the bubble can just send UserAction::kAction. There is an
optional fourth parameter to CreateForCustomUi() that is identical to the
callback in CreateForCustomAction(). If the bubble returns kAction then
this callback will be called as if this were a custom action IPH.
As with many other promos, sometimes the user will engage the feature being promoted outside the promo - for example, the user presses the toolbar or menu button the IPH is anchored to rather than interacting with the IPH.
In this case, at the very least, as with other IPH, you should call
BrowserUserEducationInterface::NotifyFeaturePromoFeatureUsed() (available on
BrowserWindow/BrowserView as well). However if you want to take additional
action (like highlighting menu elements in the app menu), you can instead call
CloseFeaturePromoAndContinue().
Both of these can close the promo and mark it as dismissed. The latter allows
you to continue the promo indefinitely while the user is having some other
experience (which could be anything). Just remember to release the
FeaturePromoHandle when you're done!