mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 20:05:49 +00:00
Bug 1547034: Add userFacingName and userFacingDescription to schema r=mythmon
Display these when available instead of generating one. We play some games here to let SinglePreferenceExperiment continue to validate according to the PreferenceExperiment schema. This is kind of ugly. Another approach might be to move the about-studies code that generates a description. I was hesitant to do this because it would mean losing the formatting. Depends on D29873 Differential Revision: https://phabricator.services.mozilla.com/D30969 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
7505d46d34
commit
f589fd6d1b
@ -29,6 +29,12 @@ class SinglePreferenceExperimentAction extends PreferenceExperimentAction {
|
||||
} = recipe.arguments;
|
||||
|
||||
const newArguments = {
|
||||
// The multiple-preference-experiment schema requires a string
|
||||
// name/description, which are necessary in the wire format, but
|
||||
// experiment objects can have null for these fields. Add some
|
||||
// filler fields here and remove them after validation.
|
||||
userFacingName: "temp-name",
|
||||
userFacingDescription: "temp-description",
|
||||
...remainingArguments,
|
||||
branches: branches.map(branch => {
|
||||
const { value, ...branchProps } = branch;
|
||||
@ -52,6 +58,9 @@ class SinglePreferenceExperimentAction extends PreferenceExperimentAction {
|
||||
throw new Error(`Transformed arguments do not match schema. Original arguments: ${JSON.stringify(recipe.arguments)}, new arguments: ${JSON.stringify(newArguments)}, schema: ${JSON.stringify(multiprefSchema)}`);
|
||||
}
|
||||
|
||||
validatedArguments.userFacingName = null;
|
||||
validatedArguments.userFacingDescription = null;
|
||||
|
||||
recipe.arguments = validatedArguments;
|
||||
|
||||
const newRecipe = {
|
||||
|
@ -173,6 +173,8 @@ const ActionSchemas = {
|
||||
type: "object",
|
||||
required: [
|
||||
"slug",
|
||||
"userFacingName",
|
||||
"userFacingDescription",
|
||||
"branches",
|
||||
],
|
||||
properties: {
|
||||
@ -181,6 +183,16 @@ const ActionSchemas = {
|
||||
type: "string",
|
||||
pattern: "^[A-Za-z0-9\\-_]+$",
|
||||
},
|
||||
userFacingName: {
|
||||
description: "User-facing name of the experiment",
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
},
|
||||
userFacingDescription: {
|
||||
description: "User-facing description of the experiment",
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
},
|
||||
experimentDocumentUrl: {
|
||||
description: "URL of a document describing the experiment",
|
||||
type: "string",
|
||||
|
@ -251,20 +251,28 @@ class PreferenceStudyListItem extends React.Component {
|
||||
render() {
|
||||
const { study, translations } = this.props;
|
||||
|
||||
// Assume there is exactly one preference (old-style preference experiment).
|
||||
const [preferenceName, { preferenceValue }] = Object.entries(study.preferences)[0];
|
||||
// Sanitize the values by setting them as the text content of an element,
|
||||
// and then getting the HTML representation of that text. This will have the
|
||||
// browser safely sanitize them. Use outerHTML to also include the <code>
|
||||
// element in the string.
|
||||
const sanitizer = document.createElement("code");
|
||||
sanitizer.textContent = preferenceName;
|
||||
const sanitizedPreferenceName = sanitizer.outerHTML;
|
||||
sanitizer.textContent = preferenceValue;
|
||||
const sanitizedPreferenceValue = sanitizer.outerHTML;
|
||||
const description = translations.preferenceStudyDescription
|
||||
.replace(/%(?:1\$)?S/, sanitizedPreferenceName)
|
||||
.replace(/%(?:2\$)?S/, sanitizedPreferenceValue);
|
||||
let userFacingName = study.userFacingName;
|
||||
if (!userFacingName) {
|
||||
userFacingName = study.name.replace(/-?pref-?(flip|study)-?/, "").replace(/-?study-?/, "").slice(0, 1);
|
||||
}
|
||||
|
||||
let description = study.userFacingDescription;
|
||||
if (!description) {
|
||||
// Assume there is exactly one preference (old-style preference experiment).
|
||||
const [preferenceName, { preferenceValue }] = Object.entries(study.preferences)[0];
|
||||
// Sanitize the values by setting them as the text content of an element,
|
||||
// and then getting the HTML representation of that text. This will have the
|
||||
// browser safely sanitize them. Use outerHTML to also include the <code>
|
||||
// element in the string.
|
||||
const sanitizer = document.createElement("code");
|
||||
sanitizer.textContent = preferenceName;
|
||||
const sanitizedPreferenceName = sanitizer.outerHTML;
|
||||
sanitizer.textContent = preferenceValue;
|
||||
const sanitizedPreferenceValue = sanitizer.outerHTML;
|
||||
description = translations.preferenceStudyDescription
|
||||
.replace(/%(?:1\$)?S/, sanitizedPreferenceName)
|
||||
.replace(/%(?:2\$)?S/, sanitizedPreferenceValue);
|
||||
}
|
||||
|
||||
return (
|
||||
r("li", {
|
||||
@ -272,7 +280,7 @@ class PreferenceStudyListItem extends React.Component {
|
||||
"data-study-name": study.name,
|
||||
},
|
||||
r("div", { className: "study-icon" },
|
||||
study.name.replace(/-?pref-?(flip|study)-?/, "").replace(/-?study-?/, "").slice(0, 1)
|
||||
userFacingName,
|
||||
),
|
||||
r("div", { className: "study-details" },
|
||||
r("div", { className: "study-header" },
|
||||
|
@ -22,6 +22,14 @@
|
||||
* @typedef {Object} Experiment
|
||||
* @property {string} name
|
||||
* Unique name of the experiment
|
||||
* @property {string|null} userFacingName
|
||||
* A user-friendly name for the experiment. Null on old-style
|
||||
* single-preference experiments, which do not have a
|
||||
* userFacingName.
|
||||
* @property {string|null} userFacingDescription
|
||||
* A user-friendly description of the experiment. Null on old-style
|
||||
* single-preference experiments, which do not have a
|
||||
* userFacingDescription.
|
||||
* @property {string} branch
|
||||
* Experiment branch that the user was matched to
|
||||
* @property {boolean} expired
|
||||
@ -383,6 +391,8 @@ var PreferenceExperiments = {
|
||||
branch,
|
||||
preferences,
|
||||
experimentType = "exp",
|
||||
userFacingName = null,
|
||||
userFacingDescription = null,
|
||||
}) {
|
||||
log.debug(`PreferenceExperiments.start(${name}, ${branch})`);
|
||||
|
||||
@ -457,6 +467,8 @@ var PreferenceExperiments = {
|
||||
lastSeen: new Date().toJSON(),
|
||||
preferences,
|
||||
experimentType,
|
||||
userFacingName,
|
||||
userFacingDescription,
|
||||
};
|
||||
|
||||
store.data.experiments[name] = experiment;
|
||||
|
@ -38,6 +38,8 @@ function argumentsFactory(args) {
|
||||
const branches = defaultBranches.map(branchFactory);
|
||||
return {
|
||||
slug: "test",
|
||||
userFacingName: "Super Cool Test Experiment",
|
||||
userFacingDescription: "Test experiment from browser_actions_PreferenceExperimentAction.",
|
||||
isHighPopulation: false,
|
||||
...args,
|
||||
branches,
|
||||
|
@ -68,6 +68,8 @@ decorate_task(
|
||||
name: "preference-experiment",
|
||||
arguments: {
|
||||
slug: "test",
|
||||
userFacingName: null,
|
||||
userFacingDescription: null,
|
||||
isHighPopulation: false,
|
||||
branches: [{
|
||||
slug: "branch1",
|
||||
|
Loading…
Reference in New Issue
Block a user