зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1592820 - Dynamic triplets click of card should replace card on refresh r=k88hudson
Differential Revision: https://phabricator.services.mozilla.com/D52610 --HG-- extra : source : 0889c2f4171cc7c108c94243a944428d2e0e17a9
This commit is contained in:
Родитель
2003c127ad
Коммит
c828174439
|
@ -205,6 +205,7 @@ export class FirstRun extends React.PureComponent {
|
|||
flowParams={flowParams}
|
||||
onDismiss={this.closeInterrupt}
|
||||
fxaEndpoint={fxaEndpoint}
|
||||
onBlockById={props.onBlockById}
|
||||
/>
|
||||
) : null}
|
||||
{hasTriplets ? (
|
||||
|
@ -219,6 +220,7 @@ export class FirstRun extends React.PureComponent {
|
|||
UTMTerm={`${UTMTerm}-card`}
|
||||
flowParams={flowParams}
|
||||
onAction={executeAction}
|
||||
onBlockById={props.onBlockById}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
|
|
|
@ -53,6 +53,7 @@ export class Interrupt extends React.PureComponent {
|
|||
sendUserActionTelemetry={sendUserActionTelemetry}
|
||||
UTMTerm={UTMTerm}
|
||||
flowParams={flowParams}
|
||||
onBlockById={this.props.onBlockById}
|
||||
/>
|
||||
);
|
||||
case "trailhead":
|
||||
|
|
|
@ -21,7 +21,7 @@ export class Triplets extends React.PureComponent {
|
|||
this.props.document.body.classList.remove("inline-onboarding");
|
||||
}
|
||||
|
||||
onCardAction(action) {
|
||||
onCardAction(action, message) {
|
||||
let actionUpdates = {};
|
||||
const { flowParams, UTMTerm } = this.props;
|
||||
|
||||
|
@ -39,6 +39,10 @@ export class Triplets extends React.PureComponent {
|
|||
}
|
||||
|
||||
this.props.onAction({ ...action, ...actionUpdates });
|
||||
// Only block if message is in dynamic triplets experiment
|
||||
if (message.blockOnClick) {
|
||||
this.props.onBlockById(message.id);
|
||||
}
|
||||
}
|
||||
|
||||
onHideContainer() {
|
||||
|
@ -70,6 +74,7 @@ export class Triplets extends React.PureComponent {
|
|||
{cards.map(card => (
|
||||
<OnboardingCard
|
||||
key={card.id}
|
||||
message={card}
|
||||
className="trailheadCard"
|
||||
sendUserActionTelemetry={sendUserActionTelemetry}
|
||||
onAction={this.onCardAction}
|
||||
|
|
|
@ -66,6 +66,7 @@ export const FxCards = ({ cards, onCardAction, sendUserActionTelemetry }) => (
|
|||
{cards.map(card => (
|
||||
<OnboardingCard
|
||||
key={card.id}
|
||||
message={card}
|
||||
className="trailheadCard"
|
||||
sendUserActionTelemetry={sendUserActionTelemetry}
|
||||
onAction={onCardAction}
|
||||
|
@ -106,7 +107,7 @@ export class FullPageInterrupt extends React.PureComponent {
|
|||
document.body.classList.remove("welcome");
|
||||
}
|
||||
|
||||
onCardAction(action) {
|
||||
onCardAction(action, message) {
|
||||
let actionUpdates = {};
|
||||
const { flowParams, UTMTerm } = this.props;
|
||||
|
||||
|
@ -124,6 +125,10 @@ export class FullPageInterrupt extends React.PureComponent {
|
|||
}
|
||||
|
||||
this.props.onAction({ ...action, ...actionUpdates });
|
||||
// Only block if message is in dynamic triplets experiment
|
||||
if (message.blockOnClick) {
|
||||
this.props.onBlockById(message.id);
|
||||
}
|
||||
this.removeOverlay();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ export class OnboardingCard extends React.PureComponent {
|
|||
id: props.UISurface,
|
||||
};
|
||||
props.sendUserActionTelemetry(ping);
|
||||
props.onAction(props.content.primary_button.action);
|
||||
props.onAction(props.content.primary_button.action, props.message);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -3363,6 +3363,7 @@ const FxCards = ({
|
|||
sendUserActionTelemetry
|
||||
}) => react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_3___default.a.Fragment, null, cards.map(card => react__WEBPACK_IMPORTED_MODULE_3___default.a.createElement(_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_2__["OnboardingCard"], _extends({
|
||||
key: card.id,
|
||||
message: card,
|
||||
className: "trailheadCard",
|
||||
sendUserActionTelemetry: sendUserActionTelemetry,
|
||||
onAction: onCardAction,
|
||||
|
@ -3393,7 +3394,7 @@ class FullPageInterrupt extends react__WEBPACK_IMPORTED_MODULE_3___default.a.Pur
|
|||
document.body.classList.remove("welcome");
|
||||
}
|
||||
|
||||
onCardAction(action) {
|
||||
onCardAction(action, message) {
|
||||
let actionUpdates = {};
|
||||
const {
|
||||
flowParams,
|
||||
|
@ -3419,7 +3420,12 @@ class FullPageInterrupt extends react__WEBPACK_IMPORTED_MODULE_3___default.a.Pur
|
|||
|
||||
this.props.onAction({ ...action,
|
||||
...actionUpdates
|
||||
});
|
||||
}); // Only block if message is in dynamic triplets experiment
|
||||
|
||||
if (message.blockOnClick) {
|
||||
this.props.onBlockById(message.id);
|
||||
}
|
||||
|
||||
this.removeOverlay();
|
||||
}
|
||||
|
||||
|
@ -3507,7 +3513,7 @@ class OnboardingCard extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureCo
|
|||
id: props.UISurface
|
||||
};
|
||||
props.sendUserActionTelemetry(ping);
|
||||
props.onAction(props.content.primary_button.action);
|
||||
props.onAction(props.content.primary_button.action, props.message);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -3572,7 +3578,7 @@ class Triplets extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponen
|
|||
this.props.document.body.classList.remove("inline-onboarding");
|
||||
}
|
||||
|
||||
onCardAction(action) {
|
||||
onCardAction(action, message) {
|
||||
let actionUpdates = {};
|
||||
const {
|
||||
flowParams,
|
||||
|
@ -3598,7 +3604,11 @@ class Triplets extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponen
|
|||
|
||||
this.props.onAction({ ...action,
|
||||
...actionUpdates
|
||||
});
|
||||
}); // Only block if message is in dynamic triplets experiment
|
||||
|
||||
if (message.blockOnClick) {
|
||||
this.props.onBlockById(message.id);
|
||||
}
|
||||
}
|
||||
|
||||
onHideContainer() {
|
||||
|
@ -3635,6 +3645,7 @@ class Triplets extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponen
|
|||
className: `trailheadCardGrid${showContent ? " show" : ""}`
|
||||
}, cards.map(card => react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_1__["OnboardingCard"], _extends({
|
||||
key: card.id,
|
||||
message: card,
|
||||
className: "trailheadCard",
|
||||
sendUserActionTelemetry: sendUserActionTelemetry,
|
||||
onAction: this.onCardAction,
|
||||
|
@ -14650,7 +14661,8 @@ class Interrupt_Interrupt extends external_React_default.a.PureComponent {
|
|||
fxaEndpoint: fxaEndpoint,
|
||||
sendUserActionTelemetry: sendUserActionTelemetry,
|
||||
UTMTerm: UTMTerm,
|
||||
flowParams: flowParams
|
||||
flowParams: flowParams,
|
||||
onBlockById: this.props.onBlockById
|
||||
});
|
||||
|
||||
case "trailhead":
|
||||
|
@ -14868,7 +14880,8 @@ class FirstRun_FirstRun extends external_React_default.a.PureComponent {
|
|||
dispatch: dispatch,
|
||||
flowParams: flowParams,
|
||||
onDismiss: this.closeInterrupt,
|
||||
fxaEndpoint: fxaEndpoint
|
||||
fxaEndpoint: fxaEndpoint,
|
||||
onBlockById: props.onBlockById
|
||||
}) : null, hasTriplets ? external_React_default.a.createElement(Triplets["Triplets"], {
|
||||
document: props.document,
|
||||
cards: triplets,
|
||||
|
@ -14879,7 +14892,8 @@ class FirstRun_FirstRun extends external_React_default.a.PureComponent {
|
|||
sendUserActionTelemetry: sendUserActionTelemetry,
|
||||
UTMTerm: `${UTMTerm}-card`,
|
||||
flowParams: flowParams,
|
||||
onAction: executeAction
|
||||
onAction: executeAction,
|
||||
onBlockById: props.onBlockById
|
||||
}) : null);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ const TRAILHEAD_CONFIG = {
|
|||
INTERRUPTS_EXPERIMENT_PREF: "trailhead.firstrun.interruptsExperiment",
|
||||
TRIPLETS_ENROLLED_PREF: "trailhead.firstrun.tripletsEnrolled",
|
||||
DEFAULT_TRIPLET: "supercharge",
|
||||
DYNAMIC_TRIPLET_BUNDLE_LENGTH: 3,
|
||||
BRANCHES: {
|
||||
interrupts: [
|
||||
["modal_control"],
|
||||
|
@ -1214,7 +1215,7 @@ class _ASRouter {
|
|||
};
|
||||
}
|
||||
|
||||
_findAllMessages(candidateMessages, trigger) {
|
||||
_findAllMessages(candidateMessages, trigger, ordered = false) {
|
||||
const messages = candidateMessages.filter(m =>
|
||||
this.isBelowFrequencyCaps(m)
|
||||
);
|
||||
|
@ -1225,6 +1226,7 @@ class _ASRouter {
|
|||
trigger,
|
||||
context,
|
||||
onError: this._handleTargetingError,
|
||||
ordered,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1347,32 +1349,29 @@ class _ASRouter {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
while (bundledMessagesOfSameTemplate.length) {
|
||||
// Find a message that matches the targeting context - or break if there are no matching messages
|
||||
const message = await this._findMessage(
|
||||
bundledMessagesOfSameTemplate,
|
||||
trigger,
|
||||
true
|
||||
);
|
||||
if (!message) {
|
||||
/* istanbul ignore next */ // Code coverage in mochitests
|
||||
break;
|
||||
}
|
||||
// Find all messages that matches the targeting context
|
||||
const allMessages = await this._findAllMessages(
|
||||
bundledMessagesOfSameTemplate,
|
||||
trigger,
|
||||
true
|
||||
);
|
||||
|
||||
if (allMessages && allMessages.length) {
|
||||
// Retrieve enough messages needed to fill a bundle
|
||||
// Only copy the content of the message (that's what the UI cares about)
|
||||
// Also delete the message we picked so we don't pick it again
|
||||
result.push({
|
||||
content: message.content,
|
||||
id: message.id,
|
||||
order: message.order || 0,
|
||||
});
|
||||
bundledMessagesOfSameTemplate.splice(
|
||||
bundledMessagesOfSameTemplate.findIndex(msg => msg.id === message.id),
|
||||
1
|
||||
result = result.concat(
|
||||
allMessages.slice(0, bundleLength).map(message => ({
|
||||
content: message.content,
|
||||
id: message.id,
|
||||
order: message.order || 0,
|
||||
// This is used to determine whether to block when action is triggered
|
||||
// Only block for dynamic triplets experiment and when there are more messages available
|
||||
blockOnClick:
|
||||
this.state.trailheadTriplet.startsWith("dynamic") &&
|
||||
allMessages.length >
|
||||
TRAILHEAD_CONFIG.DYNAMIC_TRIPLET_BUNDLE_LENGTH,
|
||||
}))
|
||||
);
|
||||
// Stop once we have enough messages to fill a bundle
|
||||
if (result.length === bundleLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1778,7 +1778,12 @@ describe("ASRouter", () => {
|
|||
provider: testMessage1.provider,
|
||||
bundle: [
|
||||
{ content: testMessage1.content, id: testMessage1.id, order: 1 },
|
||||
{ content: testMessage2.content, id: testMessage2.id, order: 2 },
|
||||
{
|
||||
content: testMessage2.content,
|
||||
id: testMessage2.id,
|
||||
order: 2,
|
||||
blockOnClick: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
assert.calledWith(
|
||||
|
@ -1885,13 +1890,14 @@ describe("ASRouter", () => {
|
|||
});
|
||||
|
||||
describe(".includeBundle", () => {
|
||||
it("should send a message with .includeBundle property with specified length and template", async () => {
|
||||
let msg;
|
||||
beforeEach(async () => {
|
||||
let messages = [
|
||||
{
|
||||
id: "trailhead",
|
||||
template: "trailhead",
|
||||
includeBundle: {
|
||||
length: 2,
|
||||
length: 3,
|
||||
template: "foo",
|
||||
trigger: { id: "foo" },
|
||||
},
|
||||
|
@ -1901,31 +1907,142 @@ describe("ASRouter", () => {
|
|||
{
|
||||
id: "foo2",
|
||||
template: "foo",
|
||||
bundled: 2,
|
||||
bundled: 3,
|
||||
order: 2,
|
||||
trigger: { id: "foo" },
|
||||
content: { title: "Foo2", body: "Foo123-2" },
|
||||
},
|
||||
{
|
||||
id: "foo3",
|
||||
template: "foo",
|
||||
bundled: 2,
|
||||
bundled: 3,
|
||||
order: 3,
|
||||
trigger: { id: "foo" },
|
||||
content: { title: "Foo3", body: "Foo123-3" },
|
||||
},
|
||||
{
|
||||
id: "foo4",
|
||||
template: "foo",
|
||||
bundled: 3,
|
||||
order: 1,
|
||||
trigger: { id: "foo" },
|
||||
content: { title: "Foo4", body: "Foo123-4" },
|
||||
},
|
||||
{
|
||||
id: "foo5",
|
||||
template: "foo",
|
||||
bundled: 3,
|
||||
order: 4,
|
||||
trigger: { id: "foo" },
|
||||
content: { title: "Foo5", body: "Foo123-5" },
|
||||
},
|
||||
];
|
||||
|
||||
sandbox.stub(Router, "_findProvider").returns(null);
|
||||
await Router.setState({ messages });
|
||||
|
||||
const msg = fakeAsyncMessage({
|
||||
msg = fakeAsyncMessage({
|
||||
type: "TRIGGER",
|
||||
data: { trigger: { id: "firstRun" } },
|
||||
});
|
||||
await Router.onMessage(msg);
|
||||
});
|
||||
|
||||
it("should send a message with .includeBundle property with specified length and template", async () => {
|
||||
await Router.onMessage(msg);
|
||||
const [, resp] = msg.target.sendAsyncMessage.firstCall.args;
|
||||
assert.propertyVal(resp, "type", "SET_MESSAGE");
|
||||
assert.isArray(resp.data.bundle, "resp.data.bundle");
|
||||
assert.lengthOf(resp.data.bundle, 2, "resp.data.bundle");
|
||||
assert.lengthOf(resp.data.bundle, 3, "resp.data.bundle");
|
||||
});
|
||||
|
||||
it("should set blockOnClick property by default false on returned ordered bundle messages", async () => {
|
||||
const expectedBundle = [
|
||||
{
|
||||
content: { title: "Foo4", body: "Foo123-4" },
|
||||
id: "foo4",
|
||||
order: 1,
|
||||
blockOnClick: false,
|
||||
},
|
||||
{
|
||||
content: { title: "Foo2", body: "Foo123-2" },
|
||||
id: "foo2",
|
||||
order: 2,
|
||||
blockOnClick: false,
|
||||
},
|
||||
{
|
||||
content: { title: "Foo3", body: "Foo123-3" },
|
||||
id: "foo3",
|
||||
order: 3,
|
||||
blockOnClick: false,
|
||||
},
|
||||
];
|
||||
|
||||
await Router.onMessage(msg);
|
||||
const [, resp] = msg.target.sendAsyncMessage.firstCall.args;
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assert.deepEqual(resp.data.bundle[i], expectedBundle[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should set blockOnClick property true for dynamic triplet and matching messages more than 3", async () => {
|
||||
await Router.setState({ trailheadTriplet: "dynamic" });
|
||||
await Router.onMessage(msg);
|
||||
const [, resp] = msg.target.sendAsyncMessage.firstCall.args;
|
||||
const expectedBundle = [
|
||||
{
|
||||
content: { title: "Foo4", body: "Foo123-4" },
|
||||
id: "foo4",
|
||||
order: 1,
|
||||
blockOnClick: true,
|
||||
},
|
||||
{
|
||||
content: { title: "Foo2", body: "Foo123-2" },
|
||||
id: "foo2",
|
||||
order: 2,
|
||||
blockOnClick: true,
|
||||
},
|
||||
{
|
||||
content: { title: "Foo3", body: "Foo123-3" },
|
||||
id: "foo3",
|
||||
order: 3,
|
||||
blockOnClick: true,
|
||||
},
|
||||
];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assert.deepEqual(resp.data.bundle[i], expectedBundle[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should set blockOnClick property true for triplet branch name that starts with 'dynamic' and matching messages more than 3", async () => {
|
||||
await Router.setState({ trailheadTriplet: "dynamic_test" });
|
||||
await Router.onMessage(msg);
|
||||
const [, resp] = msg.target.sendAsyncMessage.firstCall.args;
|
||||
const expectedBundle = [
|
||||
{
|
||||
content: { title: "Foo4", body: "Foo123-4" },
|
||||
id: "foo4",
|
||||
order: 1,
|
||||
blockOnClick: true,
|
||||
},
|
||||
{
|
||||
content: { title: "Foo2", body: "Foo123-2" },
|
||||
id: "foo2",
|
||||
order: 2,
|
||||
blockOnClick: true,
|
||||
},
|
||||
{
|
||||
content: { title: "Foo3", body: "Foo123-3" },
|
||||
id: "foo3",
|
||||
order: 3,
|
||||
blockOnClick: true,
|
||||
},
|
||||
];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assert.deepEqual(resp.data.bundle[i], expectedBundle[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ describe("<FullPageInterrupt>", () => {
|
|||
let onBlock;
|
||||
let sandbox;
|
||||
let onAction;
|
||||
let onBlockById;
|
||||
let sendTelemetryStub;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -47,6 +48,7 @@ describe("<FullPageInterrupt>", () => {
|
|||
dispatch = sandbox.stub();
|
||||
onBlock = sandbox.stub();
|
||||
onAction = sandbox.stub();
|
||||
onBlockById = sandbox.stub();
|
||||
sendTelemetryStub = sandbox.stub();
|
||||
|
||||
dummyNode = document.createElement("body");
|
||||
|
@ -69,6 +71,7 @@ describe("<FullPageInterrupt>", () => {
|
|||
dispatch={dispatch}
|
||||
onBlock={onBlock}
|
||||
onAction={onAction}
|
||||
onBlockById={onBlockById}
|
||||
cards={CARDS}
|
||||
document={fakeDocument}
|
||||
sendUserActionTelemetry={sendTelemetryStub}
|
||||
|
@ -117,4 +120,20 @@ describe("<FullPageInterrupt>", () => {
|
|||
id: "TRAILHEAD",
|
||||
});
|
||||
});
|
||||
it("should not call blockById by default when a card button is clicked", () => {
|
||||
wrapper
|
||||
.find(OnboardingCard)
|
||||
.find("button.onboardingButton")
|
||||
.simulate("click");
|
||||
assert.notCalled(onBlockById);
|
||||
});
|
||||
it("should call blockById when blockOnClick on message is true", () => {
|
||||
CARDS[0].blockOnClick = true;
|
||||
wrapper
|
||||
.find(OnboardingCard)
|
||||
.find("button.onboardingButton")
|
||||
.simulate("click");
|
||||
assert.calledOnce(onBlockById);
|
||||
assert.calledWith(onBlockById, CARDS[0].id);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,11 +27,13 @@ describe("<Triplets>", () => {
|
|||
let sendTelemetryStub;
|
||||
let onAction;
|
||||
let onHide;
|
||||
let onBlockById;
|
||||
|
||||
async function setup() {
|
||||
sandbox = sinon.createSandbox();
|
||||
sendTelemetryStub = sandbox.stub();
|
||||
onAction = sandbox.stub();
|
||||
onBlockById = sandbox.stub();
|
||||
onHide = sandbox.stub();
|
||||
|
||||
wrapper = mount(
|
||||
|
@ -42,6 +44,7 @@ describe("<Triplets>", () => {
|
|||
hideContainer={onHide}
|
||||
onAction={onAction}
|
||||
UTMTerm="trailhead-join-card"
|
||||
onBlockById={onBlockById}
|
||||
sendUserActionTelemetry={sendTelemetryStub}
|
||||
/>
|
||||
);
|
||||
|
@ -93,4 +96,20 @@ describe("<Triplets>", () => {
|
|||
id: "TRAILHEAD",
|
||||
});
|
||||
});
|
||||
it("should not call blockById by default when a card button is clicked", () => {
|
||||
wrapper
|
||||
.find(OnboardingCard)
|
||||
.find("button.onboardingButton")
|
||||
.simulate("click");
|
||||
assert.notCalled(onBlockById);
|
||||
});
|
||||
it("should call blockById when blockOnClick on message is true", () => {
|
||||
CARDS[0].blockOnClick = true;
|
||||
wrapper
|
||||
.find(OnboardingCard)
|
||||
.find("button.onboardingButton")
|
||||
.simulate("click");
|
||||
assert.calledOnce(onBlockById);
|
||||
assert.calledWith(onBlockById, CARDS[0].id);
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче