Add suggested values parameter

Parameters may include a `suggestedvalues` property, which is rendered
in the UI for some parameter types.

TemplateData editor UI elements are implemented behind the
TemplateDataSuggestedValuesEditor feature flag.

Bug: T271897
Change-Id: I14012c79b3fa0d48c58fd8999584cc03ec03575e
This commit is contained in:
Adam Wight 2021-03-10 11:54:52 +01:00
parent fef102d53c
commit 7b32bcefb4
10 changed files with 136 additions and 7 deletions

View file

@ -6,7 +6,7 @@
https://phabricator.wikimedia.org/diffusion/ETDA/browse/master/Specification.md
**Editors**
Timo Tijhof, Trevor Parscal, James D. Forrester, Marielle Volz, Moriel Schottlender, C.Scott Ananian, eranroz
Timo Tijhof, Trevor Parscal, James D. Forrester, Marielle Volz, Moriel Schottlender, C.Scott Ananian, eranroz, Adam Wight
***
@ -199,7 +199,17 @@ The default value in wikitext (or description thereof) of a parameter as assumed
Consumers SHOULD indicate this default value to the user when inserting or editing a template.
#### 3.2.11 `example`
#### 3.2.11 `suggestedvalues`
* Value: `Array`
* Default: `[]`
A list of commonly used, suggested values.
Consumers SHOULD provide this list to the user when filling out the field, but MAY implement suggested values only for some field types.
Consumers MUST allow the user to enter free-form values not on this list.
#### 3.2.12 `example`
* Value: `null` or `InterfaceText`
An example text for the parameter, to help users fill in the proper value.

View file

@ -32,7 +32,8 @@
"PageContentSave": "TemplateDataHooks::onPageContentSave",
"ResourceLoaderRegisterModules": "TemplateDataHooks::onResourceLoaderRegisterModules",
"EditPage::showEditForm:initial": "TemplateDataHooks::onEditPage",
"ParserFetchTemplateData": "TemplateDataHooks::onParserFetchTemplateData"
"ParserFetchTemplateData": "TemplateDataHooks::onParserFetchTemplateData",
"MakeGlobalVariablesScript": "TemplateDataHooks::onMakeGlobalVariablesScript"
},
"MessagesDirs": {
"TemplateData": [
@ -149,6 +150,8 @@
"templatedata-modal-table-param-actions",
"templatedata-modal-table-param-aliases",
"templatedata-modal-table-param-autovalue",
"templatedata-modal-table-param-suggestedvalues",
"templatedata-modal-table-param-suggestedvalues-placeholder",
"templatedata-modal-table-param-default",
"templatedata-modal-table-param-deprecated",
"templatedata-modal-table-param-deprecatedValue",
@ -232,6 +235,11 @@
"config": {
"TemplateDataUseGUI": {
"value": true
},
"TemplateDataSuggestedValuesEditor": {
"description": "Temporary feature flag to enable the \"suggested values\" UI",
"value": false,
"public": true
}
},
"manifest_version": 2

View file

@ -24,6 +24,7 @@
"templatedata-doc-param-status-optional": "optional",
"templatedata-doc-param-status-required": "required",
"templatedata-doc-param-status-suggested": "suggested",
"templatedata-doc-param-suggestedvalues": "Suggested values",
"templatedata-doc-param-type": "Type",
"templatedata-doc-param-type-boolean": "Boolean",
"templatedata-doc-param-type-content": "Content",
@ -101,6 +102,8 @@
"templatedata-modal-table-param-name": "Name",
"templatedata-modal-table-param-required": "Required",
"templatedata-modal-table-param-suggested": "Suggested",
"templatedata-modal-table-param-suggestedvalues": "Suggested values",
"templatedata-modal-table-param-suggestedvalues-placeholder": "Add value...",
"templatedata-modal-table-param-type": "Type",
"templatedata-modal-table-param-uneditablefield": "Uneditable",
"templatedata-modal-title": "Template documentation editor",

View file

@ -36,6 +36,7 @@
"templatedata-doc-param-status-optional": "Displayed when a template parameter is optional (should not be a full sentence, used in a table).\n{{Identical|Optional}}",
"templatedata-doc-param-status-required": "Displayed when a template parameter is required (should not be a full sentence, used in a table).\n{{Identical|Required}}",
"templatedata-doc-param-status-suggested": "Displayed when a template parameter is suggested (should not be a full sentence, used in a table).\n{{Identical|Suggested}}",
"templatedata-doc-param-suggestedvalues": "Displayed when a template parameter has suggested values.\n{{Related|Templatedata-doc-param}}",
"templatedata-doc-param-type": "Used as a column heading in the table.\n{{Identical|Type}}",
"templatedata-doc-param-type-boolean": "A possible parameter type: Boolean\n{{Related|Templatedata-doc-param-type}}\n{{Identical|Boolean}}",
"templatedata-doc-param-type-content": "A possible parameter type: Content\n{{Related|Templatedata-doc-param-type}}\n{{Identical|Content}}",
@ -113,6 +114,8 @@
"templatedata-modal-table-param-name": "Label for a parameter property input: Name of the parameter.\n{{Identical|Name}}",
"templatedata-modal-table-param-required": "Label for a parameter property input: Required status of the parameter.\n{{Identical|Required}}",
"templatedata-modal-table-param-suggested": "Label for a parameter property input: Suggested status of the parameter.\n{{Identical|Suggested}}",
"templatedata-modal-table-param-suggestedvalues": "Label for a parameter property input: List of suggested values",
"templatedata-modal-table-param-suggestedvalues-placeholder": "Placeholder text for suggested values field.",
"templatedata-modal-table-param-type": "Label for a parameter property input: Type of the parameter.\n{{Identical|Type}}",
"templatedata-modal-table-param-uneditablefield": "Placeholder text notifying the user the field is uneditable",
"templatedata-modal-title": "Title of the edit dialog.",

View file

@ -42,6 +42,7 @@ class TemplateDataBlob {
'default',
'inherits',
'type',
'suggestedvalues',
];
private const VALID_TYPES = [
@ -367,6 +368,19 @@ class TemplateDataBlob {
$paramObj->type = 'unknown';
}
// Param.suggestedvalues
if ( isset( $paramObj->suggestedvalues ) ) {
if ( !is_array( $paramObj->suggestedvalues ) ) {
return Status::newFatal(
'templatedata-invalid-type',
"params.{$paramName}.suggestedvalues",
'array'
);
}
} else {
$paramObj->suggestedvalues = [];
}
$paramNames[] = $paramName;
}
@ -818,6 +832,20 @@ class TemplateDataBlob {
}
}
$suggestedValuesLine = '';
if ( count( $paramObj->suggestedvalues ) ) {
$suggestedValues = '';
foreach ( $paramObj->suggestedvalues as $suggestedValue ) {
$suggestedValues .= wfMessage( 'word-separator' )->inLanguage( $lang )->escaped()
. Html::element( 'code', [
'class' => 'mw-templatedata-doc-param-alias'
], $suggestedValue );
}
$suggestedValuesLine .= Html::element( 'dt', [],
wfMessage( 'templatedata-doc-param-suggestedvalues' )->inLanguage( $lang )->text()
) . Html::rawElement( 'dd', [], $suggestedValues );
}
$statusClass = '';
if ( $paramObj->deprecated ) {
$status = 'templatedata-doc-param-status-deprecated';
@ -848,6 +876,8 @@ class TemplateDataBlob {
wfMessage( 'templatedata-doc-param-desc-empty' )->inLanguage( $lang )->text()
)
. Html::rawElement( 'dl', [],
// Suggested Values
$suggestedValuesLine .
// Default
( $paramObj->default !== null ? ( Html::element( 'dt', [],
wfMessage( 'templatedata-doc-param-default' )->inLanguage( $lang )->text()

View file

@ -158,6 +158,20 @@ class TemplateDataHooks {
}
}
/**
* Include config when appropriate.
*
* @param array &$vars
* @param OutputPage $output
* @see https://www.mediawiki.org/wiki/Manual:Hooks/MakeGlobalVariablesScript
*/
public static function onMakeGlobalVariablesScript( array &$vars, OutputPage $output ) {
if ( $output->getTitle()->inNamespace( NS_TEMPLATE ) ) {
$vars['wgTemplateDataSuggestedValuesEditor'] =
$output->getConfig()->get( 'TemplateDataSuggestedValuesEditor' );
}
}
/**
* Parser hook for <templatedata>.
* If there is any JSON provided, render the template documentation on the page.

View file

@ -162,6 +162,9 @@ Model.static.getAllProperties = function ( getFullData ) {
],
default: 'unknown'
},
suggestedvalues: {
type: 'array'
},
default: {
type: 'string',
multiline: true,
@ -1066,16 +1069,17 @@ Model.prototype.outputTemplateData = function () {
}
}
break;
case 'suggestedvalues':
case 'aliases':
// Only update the aliases in if the new templatedata has an
// aliases array that isn't empty
// Only update these if the new templatedata has an
// array that isn't empty
if (
Array.isArray( this.params[ key ][ prop ] ) &&
this.params[ key ][ prop ].length > 0
) {
result.params[ name ][ prop ] = this.params[ key ][ prop ];
} else {
// If the new aliases array is empty, delete it from the original
// If the new array is empty, delete it from the original
delete result.params[ name ][ prop ];
}
break;

View file

@ -891,6 +891,7 @@ Dialog.prototype.onParamPropertyInputChange = function ( property, value ) {
if ( property === 'type' ) {
value = propInput.getMenu().findSelectedItem() ? propInput.getMenu().findSelectedItem().getData() : 'unknown';
this.toggleSuggestedValues( value );
}
if ( property === 'name' ) {
@ -903,6 +904,10 @@ Dialog.prototype.onParamPropertyInputChange = function ( property, value ) {
}
}
if ( property === 'suggestedvalues' ) {
value = propInput.getValue();
}
if ( allProps[ property ].restrict ) {
if ( value.match( allProps[ property ].restrict ) ) {
// Error! Don't fix the model
@ -946,6 +951,24 @@ Dialog.prototype.onParamPropertyInputChange = function ( property, value ) {
this.trackPropertyChange( property );
};
Dialog.prototype.toggleSuggestedValues = function ( type ) {
var suggestedValuesAllowedTypes = [
'content',
'line',
'number',
'string',
'unbalanced-wikitext',
'unknown'
];
// Don't show the suggested values field when the feature flag is
// disabled, or for inapplicable types.
this.propFieldLayout.suggestedvalues.toggle(
mw.config.get( 'wgTemplateDataSuggestedValuesEditor' ) &&
suggestedValuesAllowedTypes.indexOf( type ) !== -1
);
};
/**
* Set the parameter details in the detail panel.
*
@ -966,6 +989,9 @@ Dialog.prototype.getParameterDetails = function ( paramKey ) {
}
}
// Update suggested values field visibility
this.toggleSuggestedValues( paramData.type );
this.startParameterInputTracking( paramData );
};
@ -1093,7 +1119,7 @@ Dialog.prototype.changeParamPropertyInput = function ( paramKey, propName, value
if ( languageProps.indexOf( propName ) !== -1 ) {
propInput.setValue( value[ lang ] );
} else {
if ( prop.type === 'array' && Array.isArray( value ) ) {
if ( prop.type === 'array' && Array.isArray( value ) && prop.delimiter ) {
value = value.join( prop.delimiter );
}
propInput.setValue( value );
@ -1105,6 +1131,8 @@ Dialog.prototype.changeParamPropertyInput = function ( paramKey, propName, value
propInput.selectItem( propInput.findItemFromData( prop.default ) );
} else if ( prop.type === 'boolean' ) {
propInput.setSelected( false );
} else if ( propName === 'suggestedvalues' ) {
propInput.setValue( [] );
} else {
propInput.setValue( '' );
}
@ -1178,6 +1206,11 @@ Dialog.prototype.createParamDetails = function () {
case 'suggested':
propInput = new OO.ui.CheckboxInputWidget( config );
break;
case 'suggestedvalues':
config.allowArbitrary = true;
config.placeholder = mw.msg( 'templatedata-modal-table-param-suggestedvalues-placeholder' );
propInput = new OO.ui.TagMultiselectWidget( config );
break;
default:
if ( config.multiline === true ) {
delete config.multiline;
@ -1206,6 +1239,7 @@ Dialog.prototype.createParamDetails = function () {
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-name
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-required
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-suggested
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-suggestedvalues
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-type
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-uneditablefield
propInput.$element
@ -1228,6 +1262,7 @@ Dialog.prototype.createParamDetails = function () {
// * templatedata-modal-table-param-name
// * templatedata-modal-table-param-required
// * templatedata-modal-table-param-suggested
// * templatedata-modal-table-param-suggestedvalues
// * templatedata-modal-table-param-type
// * templatedata-modal-table-param-uneditablefield
label: mw.msg( 'templatedata-modal-table-param-' + property )
@ -1276,6 +1311,7 @@ Dialog.prototype.updateParamDetailsLanguage = function ( lang ) {
// * templatedata-modal-table-param-name
// * templatedata-modal-table-param-required
// * templatedata-modal-table-param-suggested
// * templatedata-modal-table-param-suggestedvalues
// * templatedata-modal-table-param-type
// * templatedata-modal-table-param-uneditablefield
label = mw.msg( 'templatedata-modal-table-param-' + prop, lang );

View file

@ -106,6 +106,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"example": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"deprecated": false,
"aliases": [],
"type": "unknown",
@ -139,6 +140,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"autovalue": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"deprecated": false,
"aliases": [],
"type": "line"
@ -185,6 +187,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"example": null,
"required": false,
"suggested": true,
"suggestedvalues": [],
"deprecated": false,
"aliases": [
"1"
@ -229,6 +232,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"example": null,
"required": true,
"suggested": false,
"suggestedvalues": [],
"default": {
"en": "example"
},
@ -245,6 +249,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"example": null,
"required": true,
"suggested": false,
"suggestedvalues": [],
"default": {
"en": "overridden"
},
@ -333,6 +338,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"required": false,
"example": null,
"suggested": false,
"suggestedvalues": [],
"description": null,
"deprecated": false,
"aliases": [],
@ -344,6 +350,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -356,6 +363,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -396,6 +404,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"autovalue": "{{SomeTemplate}}",
"required": true,
"suggested": false,
"suggestedvalues": [ "baz", "boo" ],
"deprecated": false,
"aliases": [ "foo", "baz" ],
"type": "line"
@ -424,6 +433,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"autovalue": "{{SomeTemplate}}",
"required": true,
"suggested": false,
"suggestedvalues": [ "baz", "boo" ],
"deprecated": false,
"aliases": [ "foo", "baz" ],
"type": "line"
@ -893,6 +903,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"required": false,
"example": null,
"suggested": false,
"suggestedvalues": [],
"description": null,
"deprecated": false,
"aliases": [],
@ -928,6 +939,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"default": "French",
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"deprecated": false,
"aliases": [],
@ -964,6 +976,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -1004,6 +1017,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -1080,6 +1094,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -1092,6 +1107,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -1104,6 +1120,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -1137,6 +1154,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -1149,6 +1167,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,
@ -1161,6 +1180,7 @@ class TemplateDataBlobTest extends MediaWikiTestCase {
"label": null,
"required": false,
"suggested": false,
"suggestedvalues": [],
"description": null,
"example": null,
"deprecated": false,

View file

@ -325,6 +325,7 @@ QUnit.test( 'Validation tools', function ( assert ) {
'description',
'example',
'type',
'suggestedvalues',
'default',
'autovalue',
'deprecated',