Add a display name to trial and ship stages (#2871)

* Add a display name to trial and ship stages

* Update trial extension name format

* changes suggested by @jrobbins
This commit is contained in:
Daniel Smith 2023-03-31 10:14:15 -07:00 коммит произвёл GitHub
Родитель 428ca2e93f
Коммит 888ed87aa0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 96 добавлений и 14 удалений

Просмотреть файл

@ -173,6 +173,7 @@ def stage_to_json_dict(
'id': stage.key.integer_id(),
'feature_id': stage.feature_id,
'stage_type': stage.stage_type,
'display_name': stage.display_name,
'intent_stage': INTENT_STAGES_BY_STAGE_TYPE.get(
stage.stage_type, INTENT_NONE),
'pm_emails': stage.pm_emails,

Просмотреть файл

@ -79,6 +79,7 @@ class StagesAPITest(testing_config.CustomTestCase):
'announcement_url': None,
'desktop_first': 100,
'desktop_last': None,
'display_name': None,
'dt_milestone_android_start': None,
'dt_milestone_desktop_start': None,
'dt_milestone_ios_start': None,
@ -175,6 +176,7 @@ class StagesAPITest(testing_config.CustomTestCase):
'intent_thread_url': 'https://example.com/intent',
'intent_to_experiment_url': 'https://example.com/intent',
'desktop_first': 100,
'display_name': None,
'desktop_last': None,
'android_first': None,
'android_last': None,
@ -227,6 +229,7 @@ class StagesAPITest(testing_config.CustomTestCase):
'intent_to_experiment_url': 'https://example.com/intent',
'intent_to_extend_experiment_url': None,
'desktop_first': 100,
'display_name': None,
'desktop_last': None,
'android_first': None,
'android_last': None,

Просмотреть файл

@ -562,13 +562,16 @@ class ChromedashFeatureDetail extends LitElement {
let numberDifferentiation = '';
if (this.previousStageTypeRendered === feStage.stage_type) {
this.sameTypeRendered += 1;
numberDifferentiation = ` (${this.sameTypeRendered})`;
numberDifferentiation = ` ${this.sameTypeRendered}`;
} else {
this.previousStageTypeRendered = feStage.stage_type;
this.sameTypeRendered = 1;
}
const name = `${processStage.name}${numberDifferentiation}`;
let name = `${processStage.name}${numberDifferentiation}`;
if (feStage.display_name) {
name = `${processStage.name}: ${feStage.display_name}`;
}
const isActive = this.feature.active_stage_id === feStage.id;
// Show a button to add a trial extension stage for origin trial stages.

Просмотреть файл

@ -1,6 +1,10 @@
import {LitElement, css, html, nothing} from 'lit';
import {ref} from 'lit/directives/ref.js';
import {showToastMessage, flattenSections, setupScrollToHash} from './utils.js';
import {
showToastMessage,
flattenSections,
setupScrollToHash,
shouldShowDisplayNameField} from './utils.js';
import './chromedash-form-table';
import './chromedash-form-field';
import {
@ -146,21 +150,28 @@ export class ChromedashGuideEditallPage extends LitElement {
return FORMS_BY_STAGE_TYPE[stageType] || null;
}
renderStageSection(formattedFeature, name, feStage, stageFields) {
renderStageSection(formattedFeature, sectionBaseName, feStage, stageFields) {
if (!stageFields) return nothing;
// Add a number differentiation if this stage type is the same as another stage.
let numberDifferentiation = '';
if (this.previousStageTypeRendered && this.previousStageTypeRendered === feStage.stage_type) {
this.sameTypeRendered += 1;
numberDifferentiation = ` (${this.sameTypeRendered})`;
numberDifferentiation = ` ${this.sameTypeRendered}`;
} else {
this.previousStageTypeRendered = feStage.stage_type;
this.sameTypeRendered = 1;
}
const sectionName = `${name}${numberDifferentiation}`;
let sectionName = `${sectionBaseName}${numberDifferentiation}`;
if (feStage.display_name) {
sectionName = `${sectionBaseName}: ${feStage.display_name} `;
}
const formFieldEls = stageFields.map(field => {
// Only show "display name" field if there is more than one stage of the same type.
if (field === 'display_name' &&
!shouldShowDisplayNameField(this.feature.stages, feStage.stage_type)) {
return nothing;
}
let value = formattedFeature[field];
if (STAGE_SPECIFIC_FIELDS.has(field)) {
value = feStage[field];
@ -226,9 +237,13 @@ export class ChromedashGuideEditallPage extends LitElement {
const extensions = feStage.extensions || [];
extensions.forEach(extensionStage => {
fieldsOnly = flattenSections(FLAT_TRIAL_EXTENSION_FIELDS);
let sectionName = FLAT_TRIAL_EXTENSION_FIELDS.name;
if (feStage.display_name) {
sectionName = ` ${FLAT_TRIAL_EXTENSION_FIELDS.name}: ${feStage.display_name} `;
}
formsToRender.push(this.renderStageSection(
formattedFeature,
`${FLAT_TRIAL_EXTENSION_FIELDS.name}`,
sectionName,
extensionStage,
fieldsOnly));
allFormFields = [...allFormFields, ...fieldsOnly];

Просмотреть файл

@ -1,6 +1,6 @@
import {LitElement, css, html, nothing} from 'lit';
import {ref} from 'lit/directives/ref.js';
import {showToastMessage, setupScrollToHash} from './utils.js';
import {setupScrollToHash, showToastMessage, shouldShowDisplayNameField} from './utils.js';
import './chromedash-form-table';
import './chromedash-form-field';
import {
@ -209,6 +209,11 @@ export class ChromedashGuideStagePage extends LitElement {
feStage = this.stage;
}
return section.fields.map(field => {
// Only show "display name" field if there is more than one stage of the same type.
if (field === 'display_name' &&
!shouldShowDisplayNameField(this.feature.stages, feStage.stage_type)) {
return nothing;
}
const featureJSONKey = ALL_FIELDS[field].name || field;
let value = formattedFeature[featureJSONKey];
if (STAGE_SPECIFIC_FIELDS.has(featureJSONKey)) {

Просмотреть файл

@ -134,12 +134,15 @@ export class ChromedashGuideVerifyAccuracyPage extends LitElement {
let numberDifferentiation = '';
if (this.previousStageTypeRendered && this.previousStageTypeRendered === feStage.stage_type) {
this.sameTypeRendered += 1;
numberDifferentiation = ` (${this.sameTypeRendered})`;
numberDifferentiation = ` ${this.sameTypeRendered}`;
} else {
this.previousStageTypeRendered = feStage.stage_type;
this.sameTypeRendered = 1;
}
const sectionName = `${name}${numberDifferentiation}`;
let sectionName = `${name}${numberDifferentiation}`;
if (feStage.display_name) {
sectionName = `${name}: ${feStage.display_name}`;
}
const formFieldEls = stageFields.map(field => {
let value = formattedFeature[field];
@ -206,9 +209,13 @@ export class ChromedashGuideVerifyAccuracyPage extends LitElement {
const extensions = feStage.extensions || [];
extensions.forEach(extensionStage => {
fieldsOnly = flattenSections(VERIFY_ACCURACY_TRIAL_EXTENSION_FIELDS);
let sectionName = VERIFY_ACCURACY_TRIAL_EXTENSION_FIELDS.name;
if (feStage.display_name) {
sectionName = `${feStage.display_name} ${VERIFY_ACCURACY_TRIAL_EXTENSION_FIELDS.name}`;
}
formsToRender.push(this.renderStageSection(
formattedFeature,
`${VERIFY_ACCURACY_TRIAL_EXTENSION_FIELDS.name}`,
sectionName,
extensionStage,
fieldsOnly));
allFormFields = [...allFormFields, ...fieldsOnly];

Просмотреть файл

@ -261,12 +261,15 @@ export class ChromedashProcessOverview extends LitElement {
let numberDifferentiation = '';
if (this.previousStageTypeRendered === feStage.stage_type) {
this.sameTypeRendered += 1;
numberDifferentiation = ` (${this.sameTypeRendered})`;
numberDifferentiation = ` ${this.sameTypeRendered}`;
} else {
this.previousStageTypeRendered = feStage.stage_type;
this.sameTypeRendered = 1;
}
const sectionName = `${processStage.name}${numberDifferentiation}`;
let sectionName = `${processStage.name}${numberDifferentiation}`;
if (feStage.display_name) {
sectionName = `${feStage.display_name} (${processStage.name})`;
}
return html`
<tr class="${isActive ?

Просмотреть файл

@ -297,6 +297,7 @@ const FLAT_ORIGIN_TRIAL_FIELDS = {
{
name: 'Origin trial',
fields: [
'display_name',
'experiment_goals',
'experiment_risks',
'ongoing_constraints',
@ -360,6 +361,7 @@ const FLAT_PREPARE_TO_SHIP_FIELDS = {
{
name: 'Prepare to ship',
fields: [
'display_name',
// Standardization
'tag_review_status',
'webview_risks',
@ -523,6 +525,7 @@ const DEPRECATION_ORIGIN_TRIAL_FIELDS = {
{
name: 'Origin trial',
fields: [
'display_name',
'experiment_goals',
'experiment_risks',
'ongoing_constraints',
@ -557,6 +560,7 @@ const DEPRECATION_PREPARE_TO_SHIP_FIELDS = {
{
name: 'Prepare to ship',
fields: [
'display_name',
'intent_to_ship_url',
'i2s_lgtms',
],

Просмотреть файл

@ -140,6 +140,7 @@ export const STAGE_SPECIFIC_FIELDS = new Set([
'intent_to_extend_experiment_url',
// Misc fields.
'display_name',
'origin_trial_feedback_url',
'finch_url',
'experiment_goals',

Просмотреть файл

@ -1260,6 +1260,25 @@ export const ALL_FIELDS = {
help_text: '',
},
'display_name': {
type: 'input',
attrs: TEXT_FIELD_ATTRS,
required: false,
label: 'Stage display name',
help_text: html`
<p>Optional. Stage name to display on the feature detail page.</p>`,
extra_help: html`
<p>
This name is only used for displaying stages on this site. Use this to differentiate stages of the same type.
</p>
<h4>Examples</h4>
<ul>
<li>Extended deprecation trial</li>
<li>Second origin trial run</li>
<li>Delayed ship for Android</li>
</ul>`,
},
'enterprise_policies': {
type: 'input',
attrs: MULTI_STRING_FIELD_ATTRS,

Просмотреть файл

@ -45,6 +45,24 @@ export function findProcessStage(feStage, process) {
return null;
}
/* Determine if the display name field should be displayed for a stage. */
export function shouldShowDisplayNameField(feStages, stageType) {
// The display name field is only available to a feature's stages
// that have more than 1 of the same stage type associated.
// It is used to differentiate those stages.
let matchingStageCount = 0;
for (let i = 0; i < feStages.length; i++) {
if (feStages[i].stage_type === stageType) {
matchingStageCount++;
// If we find two of the same stage type, then display the display name field.
if (matchingStageCount > 1) {
return true;
}
}
}
return false;
}
/* Given a process stage, find the first feature entry stage of the same type. */
export function findFirstFeatureStage(intentStage, currentStage, fe) {
if (intentStage == currentStage.intent_stage) {

Просмотреть файл

@ -226,6 +226,7 @@ class Stage(ndb.Model):
# Identifying information: what.
feature_id = ndb.IntegerProperty(required=True)
stage_type = ndb.IntegerProperty(required=True)
display_name = ndb.StringProperty()
# Pragmatic information: where and when.
browser = ndb.StringProperty() # Blank or "Chrome" for now.

Просмотреть файл

@ -27,6 +27,7 @@ class StageDict(TypedDict):
id: int
feature_id: int
stage_type: int
display_name: str
intent_stage: int
intent_thread_url: str | None

Просмотреть файл

@ -258,6 +258,7 @@ class FeatureEditHandler(basehandlers.FlaskHandler):
('rollout_platforms', 'split_str'),
('rollout_details', 'str'),
('enterprise_policies', 'split_str'),
('display_name', 'str')
]
INTENT_FIELDS: list[str] = [