Skip to content

Commit

Permalink
[ntp][fre] Add "Got It" button functionality
Browse files Browse the repository at this point in the history
When a user clicks "Got It" on their Modular NTP Desktop v1 First Run
Experience (FRE) the FRE disappears, while modules stay.

Video demo: http://recall/clips/8ac5a1d0-d609-4ad2-acf7-01990c27b4af

Change-Id: I80ad9cc455fcbecb5ba5f463e4f25d1212ce3b16
Bug: 1306704
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3523509
Reviewed-by: Tibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Commit-Queue: Paul Adedeji <pauladedeji@google.com>
Cr-Commit-Position: refs/heads/main@{#984139}
  • Loading branch information
Paul Adedeji authored and Chromium LUCI CQ committed Mar 23, 2022
1 parent 92042bd commit 1e20157
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 18 deletions.
6 changes: 3 additions & 3 deletions chrome/browser/resources/new_tab_page/modules/modules.html
Expand Up @@ -5,7 +5,7 @@
--ntp-module-container-padding-top: 16px;
}

:host([show-fre_]) #freAndModulesContainer {
:host([show-modules-fre_]) #freAndModulesContainer {
border: 1px solid var(--ntp-border-color);
border-radius: 4px;
padding: 20px;
Expand Down Expand Up @@ -96,7 +96,7 @@
}
</style>
<div id="freAndModulesContainer">
<template is="dom-if" if="[[showFre_]]">
<template is="dom-if" if="[[showModulesFre_]]">
<div id="firstRunExperience">
<div id="firstRunExperienceTitle">
$i18n{modulesFirstRunExperienceTitle}
Expand All @@ -108,7 +108,7 @@
<div class="first-run-experience-body">
$i18nRaw{modulesFirstRunExperienceBodyLine2}
</div>
<cr-button class="action-button">
<cr-button class="action-button" on-click="onModulesFreOptIn_">
$i18n{modulesFirstRunOptInButton}
</cr-button>
<cr-button class="cancel-button">
Expand Down
50 changes: 35 additions & 15 deletions chrome/browser/resources/new_tab_page/modules/modules.ts
Expand Up @@ -71,16 +71,6 @@ export class ModulesElement extends PolymerElement {
value: null,
},

/**
* When the first run experience (FRE) is disabled and modules are
* enabled, we show the modules without a FRE.
*/
showFre_: {
reflectToAttribute: true,
type: Boolean,
computed: `computeShowFre_(modulesLoaded_)`,
},

modulesLoaded_: Boolean,
modulesVisibilityDetermined_: Boolean,

Expand All @@ -92,10 +82,19 @@ export class ModulesElement extends PolymerElement {
observer: 'onModulesLoadedAndVisibilityDeterminedChange_',
},

dragEnabled_: {
modulesFreVisible_: {
type: Boolean,
value: () => loadTimeData.getBoolean('modulesDragAndDropEnabled'),
value: false,
},

/**
* When the first run experience (FRE) is disabled and modules are
* enabled, we show the modules without a FRE.
*/
showModulesFre_: {
reflectToAttribute: true,
type: Boolean,
computed: `computeShowModulesFre_(modulesFreVisible_, modulesLoaded_)`,
},

/** @private {boolean} */
Expand All @@ -104,6 +103,12 @@ export class ModulesElement extends PolymerElement {
value: () => loadTimeData.getBoolean('modulesRedesignedLayoutEnabled'),
reflectToAttribute: true,
},

dragEnabled_: {
type: Boolean,
value: () => loadTimeData.getBoolean('modulesDragAndDropEnabled'),
reflectToAttribute: true,
},
};
}

Expand All @@ -115,13 +120,15 @@ export class ModulesElement extends PolymerElement {
private disabledModules_: {all: boolean, ids: string[]};
private removedModuleData_: {message: string, undo: () => void}|null;
private modulesFirstRunExperienceEnabled_: boolean;
private showFre_: boolean;
private modulesLoaded_: boolean;
private modulesVisibilityDetermined_: boolean;
private modulesLoadedAndVisibilityDetermined_: boolean;
private modulesFreVisible_: boolean;
private showModulesFre_: boolean;
private dragEnabled_: boolean;

private setDisabledModulesListenerId_: number|null = null;
private setModulesFreVisibilityListenerId_: number|null = null;
private eventTracker_: EventTracker = new EventTracker();

override connectedCallback() {
Expand All @@ -133,14 +140,23 @@ export class ModulesElement extends PolymerElement {
this.disabledModules_ = {all, ids};
this.modulesVisibilityDetermined_ = true;
});
this.setModulesFreVisibilityListenerId_ =
NewTabPageProxy.getInstance()
.callbackRouter.setModulesFreVisibility.addListener(
(visible: boolean) => {
this.modulesFreVisible_ = visible;
});
NewTabPageProxy.getInstance().handler.updateDisabledModules();
NewTabPageProxy.getInstance().handler.updateModulesFreVisibility();
this.eventTracker_.add(window, 'keydown', this.onWindowKeydown_.bind(this));
}

override disconnectedCallback() {
super.disconnectedCallback();
NewTabPageProxy.getInstance().callbackRouter.removeListener(
assert(this.setDisabledModulesListenerId_!));
NewTabPageProxy.getInstance().callbackRouter.removeListener(
assert(this.setModulesFreVisibilityListenerId_!));
this.eventTracker_.removeAll();
}

Expand All @@ -149,10 +165,10 @@ export class ModulesElement extends PolymerElement {
this.renderModules_();
}

private computeShowFre_(): boolean {
private computeShowModulesFre_(): boolean {
return (
loadTimeData.getBoolean('modulesFirstRunExperienceEnabled') &&
this.modulesLoaded_);
this.modulesLoaded_ && this.modulesFreVisible_);
}

private appendModuleContainers_(moduleContainers: HTMLElement[]) {
Expand Down Expand Up @@ -369,6 +385,10 @@ export class ModulesElement extends PolymerElement {
new Event('customize-module', {bubbles: true, composed: true}));
}

private onModulesFreOptIn_() {
NewTabPageProxy.getInstance().handler.setModulesFreVisible(false);
}

/**
* Module is dragged by updating the module position based on the
* position of the pointer.
Expand Down
6 changes: 6 additions & 0 deletions chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
Expand Up @@ -259,6 +259,10 @@ interface PageHandler {
// Returns the order of modules or an empty array if the user has not
// reordered them before.
GetModulesOrder() => (array<string> module_ids);
// If |visible| Modular NTP Desktop v1 First Run Experience will be shown.
SetModulesFreVisible(bool visible);
// Triggers a call to |SetModulesFreVisibility|.
UpdateModulesFreVisibility();

// ======= METRICS =======
// Logs that the One Google Bar was added to the DOM / loaded in an iframe at
Expand Down Expand Up @@ -300,4 +304,6 @@ interface Page {
// Disables the modules in |ids|. If |all|, disables all modules and passes an
// empty list for |ids|.
SetDisabledModules(bool all, array<string> ids);
// Sets Modular NTP Desktop v1 First Run Experience's visibility to |visible|.
SetModulesFreVisibility(bool visible);
};
11 changes: 11 additions & 0 deletions chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
Expand Up @@ -399,6 +399,7 @@ void NewTabPageHandler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(prefs::kNtpDisabledModules, true);
registry->RegisterListPref(prefs::kNtpModulesOrder, true);
registry->RegisterBooleanPref(prefs::kNtpModulesVisible, true);
registry->RegisterBooleanPref(prefs::kNtpModulesFreVisible, true);
}

void NewTabPageHandler::SetMostVisitedSettings(bool custom_links_enabled,
Expand Down Expand Up @@ -652,6 +653,16 @@ void NewTabPageHandler::GetModulesOrder(GetModulesOrderCallback callback) {
std::move(callback).Run(std::move(module_ids));
}

void NewTabPageHandler::UpdateModulesFreVisibility() {
page_->SetModulesFreVisibility(
profile_->GetPrefs()->GetBoolean(prefs::kNtpModulesFreVisible));
}

void NewTabPageHandler::SetModulesFreVisible(bool visible) {
profile_->GetPrefs()->SetBoolean(prefs::kNtpModulesFreVisible, visible);
UpdateModulesFreVisibility();
}

void NewTabPageHandler::OnPromoDataUpdated() {
if (promo_load_start_time_.has_value()) {
base::TimeDelta duration = base::TimeTicks::Now() - *promo_load_start_time_;
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
Expand Up @@ -106,6 +106,8 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler,
void OnModulesLoadedWithData() override;
void SetModulesOrder(const std::vector<std::string>& module_ids) override;
void GetModulesOrder(GetModulesOrderCallback callback) override;
void UpdateModulesFreVisibility() override;
void SetModulesFreVisible(bool visible) override;
void OnAppRendered(double time) override;
void OnOneGoogleBarRendered(double time) override;
void OnPromoRendered(double time,
Expand Down
Expand Up @@ -75,6 +75,7 @@ class MockPage : public new_tab_page::mojom::Page {

MOCK_METHOD1(SetTheme, void(new_tab_page::mojom::ThemePtr));
MOCK_METHOD2(SetDisabledModules, void(bool, const std::vector<std::string>&));
MOCK_METHOD1(SetModulesFreVisibility, void(bool));

mojo::Receiver<new_tab_page::mojom::Page> receiver_{this};
};
Expand Down Expand Up @@ -672,3 +673,25 @@ TEST_F(NewTabPageHandlerTest, GetModulesOrder) {
handler_->GetModulesOrder(callback.Get());
EXPECT_THAT(module_ids, ElementsAre("foo", "bar", "baz"));
}

TEST_F(NewTabPageHandlerTest, UpdateModulesFreVisibility) {
bool expected_visibility = true;
profile_->GetPrefs()->SetBoolean(prefs::kNtpModulesFreVisible,
expected_visibility);

EXPECT_EQ(profile_->GetPrefs()->GetBoolean(prefs::kNtpModulesFreVisible),
expected_visibility);

expected_visibility = false;
EXPECT_CALL(mock_page_, SetModulesFreVisibility)
.Times(1)
.WillOnce(testing::Invoke(
[&](bool arg) { EXPECT_EQ(expected_visibility, arg); }));

handler_->SetModulesFreVisible(expected_visibility);

EXPECT_EQ(profile_->GetPrefs()->GetBoolean(prefs::kNtpModulesFreVisible),
expected_visibility);

mock_page_.FlushForTesting();
}
2 changes: 2 additions & 0 deletions chrome/common/pref_names.cc
Expand Up @@ -1926,6 +1926,8 @@ const char kNtpDisabledModules[] = "NewTabPage.DisabledModules";
const char kNtpModulesOrder[] = "NewTabPage.ModulesOrder";
// Whether NTP modules are visible.
const char kNtpModulesVisible[] = "NewTabPage.ModulesVisible";
// Whether Modular NTP Desktop v1 First Run Experience is visible.
const char kNtpModulesFreVisible[] = "NewTabPage.ModulesFreVisible";
// List of promos that the user has dismissed while on the NTP.
const char kNtpPromoBlocklist[] = "ntp.promo_blocklist";
// Whether the promo is visible.
Expand Down
1 change: 1 addition & 0 deletions chrome/common/pref_names.h
Expand Up @@ -644,6 +644,7 @@ extern const char kNtpCustomBackgroundLocalToDevice[];
extern const char kNtpDisabledModules[];
extern const char kNtpModulesOrder[];
extern const char kNtpModulesVisible[];
extern const char kNtpModulesFreVisible[];
extern const char kNtpPromoBlocklist[];
extern const char kNtpPromoVisible[];
extern const char kNtpSearchSuggestionsBlocklist[];
Expand Down
58 changes: 58 additions & 0 deletions chrome/test/data/webui/new_tab_page/modules/modules_test.ts
Expand Up @@ -100,6 +100,62 @@ suite('NewTabPageModulesModulesTest', () => {
});
});

[true, false].forEach(visible => {
test(`first run experience shows if modules ${visible}`, async () => {
// Arrange.
const fooDescriptor =
new ModuleDescriptor('foo', 'Foo', initNullModule);
const barDescriptor =
new ModuleDescriptor('bar', 'Bar', initNullModule);
const bazDescriptor =
new ModuleDescriptor('baz', 'Baz', initNullModule);
moduleRegistry.setResultFor(
'getDescriptors', [fooDescriptor, barDescriptor, bazDescriptor]);
// Act.
const modulesElement = await createModulesElement([
{
descriptor: fooDescriptor,
element: createElement(),
},
{
descriptor: barDescriptor,
element: createElement(),
}
]);
callbackRouterRemote.setDisabledModules(
!visible, [barDescriptor.id, bazDescriptor.id]);
callbackRouterRemote.setModulesFreVisibility(visible);
await callbackRouterRemote.$.flushForTesting();

// Assert.
const moduleWrappers =
modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
const moduleWrapperContainers =
modulesElement.shadowRoot!.querySelectorAll('.module-container');
assertEquals(2, moduleWrappers.length);
assertEquals(2, moduleWrapperContainers.length);
assertNotStyle(moduleWrappers[0]!, 'display', 'none');
if (visible) {
assertNotStyle(moduleWrapperContainers[0]!, 'display', 'none');
} else {
assertStyle(moduleWrapperContainers[0]!, 'display', 'none');
}
assertNotStyle(moduleWrappers[1]!, 'display', 'none');
assertStyle(moduleWrapperContainers[1]!, 'display', 'none');
assertNotStyle(moduleWrappers[0]!, 'cursor', 'grab');
assertNotStyle(moduleWrappers[1]!, 'cursor', 'grab');
const histogram = 'NewTabPage.Modules.EnabledOnNTPLoad';
assertEquals(1, metrics.count(`${histogram}.foo`, visible));
assertEquals(1, metrics.count(`${histogram}.bar`, false));
assertEquals(1, metrics.count(`${histogram}.baz`, false));
assertEquals(
1, metrics.count('NewTabPage.Modules.VisibleOnNTPLoad', visible));
assertEquals(1, handler.getCallCount('updateDisabledModules'));
assertEquals(1, handler.getCallCount('onModulesLoadedWithData'));
assertEquals(1, handler.getCallCount('updateModulesFreVisibility'));
});
});

test(`clicking customize chrome link sends event`, async () => {
// Arrange.
const fooDescriptor = new ModuleDescriptor('foo', 'Foo', initNullModule);
Expand All @@ -110,6 +166,8 @@ suite('NewTabPageModulesModulesTest', () => {
element: createElement(),
},
]);
callbackRouterRemote.setModulesFreVisibility(true);
await callbackRouterRemote.$.flushForTesting();
const customizeModule = capture(modulesElement, 'customize-module');
render(modulesElement);

Expand Down

0 comments on commit 1e20157

Please sign in to comment.