Dana Fried | 7b8baeb | 2022-06-28 14:25:13 | [diff] [blame] | 1 | # User Education component library |
| 2 | |
Dana Fried | 5c1e4f2 | 2022-08-01 20:12:24 | [diff] [blame^] | 3 | This library contains the code that will allow you to implement |
| 4 | **In-Product-Help (IPH)** and **Tutorials** in any framework, as well as display |
| 5 | the **"New" Badge** on menus and labels. |
Dana Fried | 7b8baeb | 2022-06-28 14:25:13 | [diff] [blame] | 6 | |
Dana Fried | 5c1e4f2 | 2022-08-01 20:12:24 | [diff] [blame^] | 7 | ## Upstream dependencies |
| 8 | |
| 9 | Familiarity with these libraries are strongly recommended; feel free to reach |
| 10 | out to their respective OWNERS if you have any questions. |
| 11 | |
| 12 | * [UI Interaction](/ui/base/interaction/README.md) |
| 13 | * [ElementTracker](/ui/base/interaction/element_tracker.h) - supplies anchor |
| 14 | points for help bubbles |
| 15 | * [InteractionSequence](/ui/base/interaction/interaction_sequence.h) - |
| 16 | describes the situations in which a Tutorial advances to the next step |
| 17 | * [Feature Engagement](/components/feature_engagement/README.md) - used to |
| 18 | evaluate triggering conditions for IPH and New Badge. |
| 19 | |
| 20 | ## Directory structure |
| 21 | |
Dana Fried | 7b8baeb | 2022-06-28 14:25:13 | [diff] [blame] | 22 | * [common](./common) - contains platform- and framework-agnostic APIs for |
Dana Fried | 5c1e4f2 | 2022-08-01 20:12:24 | [diff] [blame^] | 23 | working with `HelpBubble`s, **IPH**, and **Tutorials**. |
| 24 | * [test](./test) - contains common code for testing user education primitives |
Dana Fried | 7b8baeb | 2022-06-28 14:25:13 | [diff] [blame] | 25 | * [views](./views) - contains code required to display a `HelpBubble` in a |
Dana Fried | 5c1e4f2 | 2022-08-01 20:12:24 | [diff] [blame^] | 26 | Views-based UI, as well as **"New" Badge** primitives. |
Dana Fried | 7b8baeb | 2022-06-28 14:25:13 | [diff] [blame] | 27 | * [webui](./webui/README.md) - contains code required to display a `HelpBubble` |
| 28 | on a WebUI surface. |
| 29 | |
Dana Fried | 5c1e4f2 | 2022-08-01 20:12:24 | [diff] [blame^] | 30 | # Programming API |
| 31 | |
| 32 | ## Help bubbles |
| 33 | |
| 34 | The core presentation element for both IPH and Tutorials is the |
| 35 | [HelpBubble](./common/help_bubble.h). A `HelpBubble` is a blue bubble that |
| 36 | appears anchored to an element in your application's UI and which contains |
| 37 | information about that element. For example, a `HelpBubble` might appear |
| 38 | underneath the profile button the first time the user starts Chrome after |
| 39 | adding a second profile, showing the user how they can switch between profiles. |
| 40 | |
| 41 | Different UI frameworks have different `HelpBubble` implementations; for |
| 42 | example, [HelpBubbleViews](./views/help_bubble_factory_views.h). Each type of |
| 43 | `HelpBubble` is created by a different |
| 44 | [HelpBubbleFactory](./common/help_bubble_factory.h), which is registered at |
| 45 | startup in the global |
| 46 | [HelpBubbleFactoryRegistry](./common/help_bubble_factory_registry.h). So for |
| 47 | example, Chrome registers separate factories for Views and WebUI, and on Mac |
| 48 | a third factory that can attach a Views-based `HelpBubble` to a Mac native menu. |
| 49 | |
| 50 | To actually show the bubble, the `HelpBubbleFactoryRegistry` needs two things: |
| 51 | * The `TrackedElement` the bubble will be anchored to |
| 52 | * The [HelpBubbleParams](./common/help_bubble_params.h) describing the bubble |
| 53 | |
| 54 | You will notice that this is an extremely bare-bones system. ***You are not |
| 55 | expected to call `HelpBubbleFactoryRegistry` directly!*** Rather, the IPH and |
| 56 | Tutorial systems use this API to show help bubbles. |
| 57 | |
| 58 | ## In-Product Help (IPH) |
| 59 | |
| 60 | In-Product Help is the simpler of the two ways to display help bubbles, and can |
| 61 | even be the entry point for a Tutorial. |
| 62 | |
| 63 | IPH are: |
| 64 | * **Spontaneous** - they are shown to the user when a set of conditions are |
| 65 | met; the user does not choose to see them. |
| 66 | * **Rate-limited** - the user will only ever see a specific IPH a certain |
| 67 | number of times, and will only see a certain total number of different IPH |
| 68 | per session. |
| 69 | * **Simple** - only a small number of templates approved by UX are available, |
| 70 | for different kinds of User Education journeys. |
| 71 | |
| 72 | Your application will provide a |
| 73 | [FeaturePromoController](./common/feature_promo_controller.h) with a |
| 74 | [FeaturePromoRegistry](./common/feature_promo_registry.h). In order to add a new |
| 75 | IPH, you will need to: |
| 76 | 1. Add the `base::Feature` corresponding to the IPH. |
| 77 | 2. Register the |
| 78 | [FeaturePromoSpecification](./common/feature_promo_specification.h) |
| 79 | describing your IPH journey (see below). |
| 80 | 3. Configure the Feature Engagement backend for your IPH journey |
| 81 | ([see documentation](/components/feature_engagement/README.md)). |
| 82 | 4. Put hooks in your code: |
| 83 | * Call `FeaturePromoController::MaybeShowPromo()` at the point in the code |
| 84 | when the promo should trigger, adding feature-specific logic for when the |
| 85 | promo is appropriate. |
| 86 | * Add additional calls to `feature_engagement::Tracker::NotifyEvent()` for |
| 87 | events that should affect whether the IPH should display. |
| 88 | * These should also be referenced in the Feature Engagement configuration. |
| 89 | * This should include the user actually engaging with the feature being |
| 90 | promo'd. |
| 91 | * You can retrieve the tracker via |
| 92 | `FeaturePromoControllerCommon::feature_engagement_tracker()`. |
| 93 | * Optionally: add calls to `FeaturePromoController::CloseBubble()` or |
| 94 | `FeaturePromoController::CloseBubbleAndContinuePromo()` to |
| 95 | programmatically end the promo when the user engages your feature. |
| 96 | 5. Enable the feature via a trade study or Finch. |
| 97 | |
| 98 | ### Registering your IPH |
| 99 | |
| 100 | You will want to create a `FeaturePromoSpecification` and register it with |
| 101 | `FeaturePromoRegistry::RegisterFeature()`. There should be a common function |
| 102 | your application uses to register IPH journeys during startup; in Chrome it's |
| 103 | `MaybeRegisterChromeFeaturePromos()`. |
| 104 | |
| 105 | There are several factory methods on FeaturePromoSpecification for different |
| 106 | types of IPH: |
| 107 | * **CreateForToastPromo** - creates a small, short-lived promo with no buttons |
| 108 | that disappears after a short time. |
| 109 | * These are designed to point out a specific UI element; you will not expect |
| 110 | the user to interact with the bubble. |
| 111 | * Because of this a screen reader message and accelerator to access the |
| 112 | relevant feature are required; this will be used to make sure that screen |
| 113 | reader users can find the thing the bubble is talking about. |
| 114 | * **CreateForSnoozePromo** - creates a promo with "got it" and "remind me |
| 115 | later" buttons and if the user picks the latter, snoozes the promo so it can |
| 116 | be displayed again later. |
| 117 | * **CreateForTutorialPromo** - similar to `CreateForSnoozePromo()` except that |
| 118 | the "got it" button is replaced by a "learn more" button that launches a |
| 119 | Tutorial. |
| 120 | * **CreateForLegacyPromo (DEPRECATED)** - creates a toast-like promo with no |
| 121 | buttons, but which does not require accessible text and has no or a long |
| 122 | timeout. *For backwards compatibility with older promos; do not use.* |
| 123 | |
| 124 | You may also call the following methods to add additional features to a bubble: |
| 125 | * **SetBubbleTitleText()** - adds an optional title to the bubble; this will |
| 126 | be in a larger font above the body text. |
| 127 | * **SetBubbleIcon()** - adds an optional icon to the bubble; this will be |
| 128 | displayed to the left (right in RTL) of the title/body. |
| 129 | * **SetBubbleArrow()** - sets the position of the arrow relative to the |
| 130 | bubble; this in turn changes the bubble's default orientation relative to |
| 131 | its anchor. |
| 132 | |
| 133 | These are advanced features |
| 134 | * **SetInAnyContext()** - allows the system to search for the anchor element |
| 135 | in any context rather than only the window in which the IPH is triggered. |
| 136 | * **SetAnchorElementFilter()** - allows the system to narrow down the anchor |
| 137 | from a collection of candidates, if there is more than one element maching |
| 138 | the anchor's `ElementIdentifier`. |
| 139 | |
| 140 | ## Tutorials |
| 141 | |
| 142 | [TBD] |
| 143 | |
| 144 | ## New Badge |
| 145 | |
| 146 | [TBD] |
| 147 | |
| 148 | ## Adding User Education to your application |
| 149 | |
| 150 | There are a number of virtual methods that must be implemented before you can |
| 151 | use these User Education libraries in a new application, mostly centered around |
| 152 | localization, accelerators, and global input focus. |
| 153 | |
| 154 | Fortunately for Chromium developers, the browser already has the necessary |
| 155 | support built in for Views, WebUI, and Mac-native context menus. You may refer |
| 156 | to |
Dana Fried | 7b8baeb | 2022-06-28 14:25:13 | [diff] [blame] | 157 | [browser_user_education_service](/chrome/browser/ui/views/user_education/browser_user_education_service.h) |
| 158 | for an example that could be extended to other (especially Views-based) |
Dana Fried | 5c1e4f2 | 2022-08-01 20:12:24 | [diff] [blame^] | 159 | platforms such as ChromeOS. |