jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/362180 )
Change subject: Multi-variant form representations.
......................................................................
Multi-variant form representations.
Integrates the RepresentationsWidget into the formview.
Bug: T165575
Change-Id: I5dda249fb2121db3b29804e202b9f90d3755a968
---
M extension.json
M resources/jquery.wikibase.lexemeformview.js
M resources/lexeme.css
M resources/templates.php
A resources/widgets/RepresentationWidget.js
M src/View/LexemeFormsView.php
M src/WikibaseLexeme.hooks.php
M tests/browser/features/forms.feature
M tests/browser/features/step_definitions/forms_steps.rb
M tests/browser/features/support/pages/lexeme_page.rb
M tests/phpunit/mediawiki/View/LexemeFormsViewTest.php
M tests/qunit/jquery.wikibase.lexemeformview.tests.js
A tests/qunit/widgets/RepresentationWidget.tests.js
13 files changed, 669 insertions(+), 117 deletions(-)
Approvals:
Jonas Kress (WMDE): Looks good to me, approved
jenkins-bot: Verified
diff --git a/extension.json b/extension.json
index 90cf4a5..d68d699 100644
--- a/extension.json
+++ b/extension.json
@@ -78,7 +78,8 @@
"dependencies": [
"jquery.ui.EditableTemplatedWidget",
"wikibase.templates.lexeme",
- "jquery.wikibase.grammaticalfeatureview"
+ "jquery.wikibase.grammaticalfeatureview",
+ "wikibase.lexeme.widgets.RepresentationWidget"
],
"messages": [
"wikibase-lexeme-empty-form-representation",
@@ -144,6 +145,16 @@
"wikibase-remove"
]
},
+ "wikibase.lexeme.widgets.RepresentationWidget": {
+ "scripts": "widgets/RepresentationWidget.js",
+ "dependencies": [
+ "vue"
+ ],
+ "messages":[
+ "wikibase-add",
+ "wikibase-remove"
+ ]
+ },
"wikibase.lexeme.widgets.GlossWidget": {
"scripts": "widgets/GlossWidget.js",
"dependencies": [
diff --git a/resources/jquery.wikibase.lexemeformview.js
b/resources/jquery.wikibase.lexemeformview.js
index e0c3108..6299ab8 100644
--- a/resources/jquery.wikibase.lexemeformview.js
+++ b/resources/jquery.wikibase.lexemeformview.js
@@ -3,6 +3,9 @@
var PARENT = $.ui.EditableTemplatedWidget;
+ /** @type {wikibase.lexeme.widgets.RepresentationWidget} */
+ var RepresentationWidget = require(
'wikibase.lexeme.widgets.RepresentationWidget' );
+
/** @type {wikibase.datamodel.TermMap}*/
var TermMap = wb.datamodel.TermMap;
/** @type {wikibase.datamodel.Term}*/
@@ -34,19 +37,19 @@
template: 'wikibase-lexeme-form',
templateParams: [
function () {
- return 'some lang';
+ var $container = $( '<span/>' );
+ this.deferredFormWithId.promise().then(
function ( form ) {
+ $container.text( form.getId() );
+ } );
+
+ return $container;
},
'',
- function () {
- return '';
- },
function () {
return mw.wbTemplate(
'wikibase-lexeme-form-grammatical-features', '' );
},
function () {
var $container = $( '<div/>' );
- this.deferredFormWithId = $.Deferred();
-
this.deferredFormWithId.promise().then(
function ( form ) {
var $header = $( '<h2/>'
).applyTemplate(
'wb-section-heading',
@@ -70,9 +73,9 @@
}
],
templateShortCuts: {
- $text: '.wikibase-lexeme-form-text',
$id: '.wikibase-lexeme-form-id',
- $grammaticalFeatures:
'.wikibase-lexeme-form-grammatical-features'
+ $grammaticalFeatures:
'.wikibase-lexeme-form-grammatical-features',
+ $representations: '.form-representations'
},
inputNodeName: 'TEXTAREA',
api: null,
@@ -86,6 +89,8 @@
_grammaticalFeatureView: null,
+ _representationsWidget: null,
+
/**
* This method acts as a setter if it is given a LexemeForm
object.
* Otherwise it returns its value if it is not in edit mode and
returns a new LexemeForm from its
@@ -98,11 +103,9 @@
if ( form instanceof
wikibase.lexeme.datamodel.LexemeForm ) {
this.option( 'value', form );
this._grammaticalFeatureView.value(
form.getGrammaticalFeatures() );
- if ( this.deferredFormWithId ) {
- if ( form.getId() ) {
-
this.deferredFormWithId.resolve( form );
- this.deferredFormWithId = null;
- }
+ if ( this.deferredFormWithId && form.getId() ) {
+ this.deferredFormWithId.resolve( form );
+ this.deferredFormWithId = null;
}
this.draw();
return;
@@ -112,21 +115,16 @@
return this.options.value;
}
- var representations = new TermMap( {
- en: new Term(
- 'en',
- this.$text.children( this.inputNodeName
).val()
- )
- } );
-
return new wikibase.lexeme.datamodel.LexemeForm(
this.options.value ? this.options.value.getId()
: null,
- representations,
+ arrayToTermMap(
this._representationsWidget.representations ),
this._grammaticalFeatureView ?
this._grammaticalFeatureView.value() : []
);
},
_create: function () {
+ this.deferredFormWithId = $.Deferred();
+
PARENT.prototype._create.call( this );
this._grammaticalFeatureView =
this._buildGrammaticalFeatureView();
@@ -134,6 +132,8 @@
this.value(),
$( '.wikibase-statementgrouplistview',
this.element )
);
+
+ this._buildRepresentations( this.value() );
},
_buildGrammaticalFeatureView: function
buildGrammaticalFeatureView() {
@@ -147,8 +147,6 @@
api: self.options.api
} );
- //FIXME add representation change propagation
-
this.$grammaticalFeatures.on(
'grammaticalfeatureviewchange', function () {
self._trigger( 'change' );
} );
@@ -158,22 +156,37 @@
_startEditing: function () {
this._inEditMode = true;
- this._grammaticalFeatureView.startEditing();// FIXME
this line breaks edit mode when adding lexeme form
+ this._grammaticalFeatureView.startEditing();
+ this._representationsWidget.edit();
return this.draw();
},
_stopEditing: function ( dropValue ) {
this._inEditMode = false;
- if ( dropValue &&
this.options.value.getRepresentation() === '' ) {
- this.$text.children( this.inputNodeName ).val(
'' );
+ if ( dropValue ) {
+ this._representationsWidget.representations =
termMapToArray( this.value().getRepresentations() );
}
this._grammaticalFeatureView.stopEditing( dropValue );
+ this._representationsWidget.stopEditing();
return this.draw();
},
isInEditMode: function () {
return this._inEditMode;
+ },
+
+ _buildRepresentations: function ( form ) {
+ var representations = form ? termMapToArray(
form.getRepresentations() ) : [];
+
+ this._representationsWidget =
RepresentationWidget.create(
+ representations,
+ this.$representations[ 0 ],
+ '#representation-widget-vue-template',
+ function () {
+ this._trigger( 'change' );
+ }.bind( this )
+ );
},
/**
@@ -187,7 +200,6 @@
}
if ( !this.isInEditMode() && !value ) {
- this.$text.text( mw.msg(
'wikibase-lexeme-empty-form-representation' ) );
// Apply lang and dir of UI language
// instead language of that row
var userLanguage = mw.config.get(
'wgUserLanguage' );
@@ -197,30 +209,38 @@
return deferred.resolve().promise();
}
- if ( !this.isInEditMode() ) {
- this.$text.text(
value.getRepresentations().getItemByKey( 'en' ).getText() );
- this.$id.text( ' (' + value.getId() + ')' ); //
TODO: whitespace and brackets (?) should be i18nable
-
- return deferred.resolve().promise();
- }
-
- var $input = $( document.createElement(
this.options.inputNodeName ) )
- .attr( 'placeholder', mw.msg(
'wikibase-lexeme-enter-form-representation' ) )
- .on( 'change', function () { this._trigger(
'change' ); }.bind( this ) );
-
- if ( value ) {
- $input.val(
value.getRepresentations().getItemByKey( 'en' ).getText() );
- }
-
- if ( $.fn.inputautoexpand ) {
- $input.inputautoexpand( {
- suppressNewLine: true
- } );
- }
-
- this.$text.empty().append( $input );
-
return deferred.resolve().promise();
}
} );
+
+ function arrayToTermMap( representations ) {
+ var result = new wikibase.datamodel.TermMap();
+
+ representations.forEach( function ( representation ) {
+ try {
+ result.setItem(
+ representation.language,
+ new wikibase.datamodel.Term(
representation.language, representation.value )
+ );
+ } catch ( e ) {
+ // ignore
+ }
+ } );
+
+ return result;
+ }
+
+ /**
+ * @param {wikibase.datamodel.TermMap} representations
+ * @return {Array}
+ */
+ function termMapToArray( representations ) {
+ var result = [];
+
+ representations.each( function ( language, term ) {
+ result.push( { language: term.getLanguageCode(), value:
term.getText() } );
+ } );
+
+ return result;
+ }
}( jQuery, mediaWiki, wikibase ) );
diff --git a/resources/lexeme.css b/resources/lexeme.css
index 6d6fb72..d0bad3d 100644
--- a/resources/lexeme.css
+++ b/resources/lexeme.css
@@ -18,6 +18,14 @@
clear: left;
}
+.wikibase-lexeme-form-header {
+ display: flex;
+ align-items: center;
+}
+.wikibase-lexeme-form-id {
+ font-size: 2em;
+}
+
.wikibase-lexeme-forms .wikibase-lexeme-form-representation {
clear: left;
margin-left: 10px; /* same as .wb-section-heading */
@@ -35,10 +43,6 @@
font-size: 0.9em;
height: 1.5em;
vertical-align: middle;
-}
-
-.wikibase-lexeme-form-representation .wikibase-lexeme-form-id {
- color: #72777d;
}
.wikibase-lexeme-forms-section > .wikibase-toolbar-container {
@@ -174,6 +178,116 @@
cursor: pointer;
}
+.representation-widget {
+ display: flex;
+ margin: 0 2em;
+}
+
+.representation-widget_representation-list {
+ margin: 0 !important;
+ padding: 0 !important;
+ list-style: none;
+ display: flex;
+ flex-wrap: wrap;
+ flex-grow: 1;
+}
+
+.representation-widget_representation {
+ display: flex;
+ flex-direction: column;
+ border-left: 6px solid #ededed;
+ padding: 0 9px;
+ margin: 11px 0;
+}
+
+.representation-widget_representation-value {
+ font-size: 2em;
+ font-weight: bold;
+}
+
+.representation-widget_representation-language {
+ font-size: 1em;
+}
+
+.representation-widget_edit-area {
+ display: flex;
+ border: 2px solid #99ccff;
+}
+
+.representation-widget_controls {
+ width: 100px;
+ padding: 20px 40px;
+ background: #dae8fc;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.representation-widget_control {
+ background: transparent;
+ border: none;
+ font-size: 1em;
+ margin: 10px;
+ cursor: pointer;
+ color: #0d4c99;
+}
+
+.representation-widget_representation-edit-box {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ border-left: 6px solid #dae8fc;
+ padding: 0 45px 0 9px;
+ margin: 11px 0;
+
+ height: 132px;
+}
+
+.representation-widget_representation-value-input {
+ resize: horizontal;
+ min-width: 120px;
+ padding-right: 20px;
+
+ font-size: 2em;
+}
+
+.representation-widget_representation-value-input:active {
+ width: auto;
+}
+
+.representation-widget_representation-language-input {
+ resize: horizontal;
+ min-width: 120px;
+ font-size: 1em;
+ padding-right: 20px;
+}
+
+.representation-widget_representation-language-input:active {
+ width: auto;
+}
+
+.representation-widget_representation-remove {
+ border: none;
+ background: transparent;
+ color: #074c99;
+ font-size: 2em;
+ cursor: pointer;
+
+ position: absolute;
+ top: -11px;
+ right: 23px;
+}
+
+.representation-widget_add {
+ height: 100%;
+ border: none;
+ font-size: 2em;
+ text-align: center;
+ background: #dae8fc;
+ cursor: pointer;
+}
+
.wikibase-lexeme-senses .wikibase-lexeme-sense {
position: relative;
clear: left;
@@ -254,4 +368,4 @@
.wikibase-lexeme-sense > .wikibase-edittoolbar-container {
display: none; /* FIXME: temporarily hidden until entering/editing
glosses is possible */
-}
\ No newline at end of file
+}
diff --git a/resources/templates.php b/resources/templates.php
index 885d64d..b3d9136 100644
--- a/resources/templates.php
+++ b/resources/templates.php
@@ -16,12 +16,12 @@
$templates['wikibase-lexeme-form'] = <<<'HTML'
<div class="wikibase-lexeme-form">
- <h3 class="wikibase-lexeme-form-representation" lang="$1">
- <span class="wikibase-lexeme-form-text">$2</span>
- <span class="wikibase-lexeme-form-id wikibase-title-id">
$3</span>
- </h3>
+ <div class="wikibase-lexeme-form-header">
+ <div class="wikibase-lexeme-form-id">$1</div>
+ <div class="form-representations">$2</div>
+ </div>
+ $3
$4
- $5
</div>
HTML;
diff --git a/resources/widgets/RepresentationWidget.js
b/resources/widgets/RepresentationWidget.js
new file mode 100644
index 0000000..208f372
--- /dev/null
+++ b/resources/widgets/RepresentationWidget.js
@@ -0,0 +1,78 @@
+module.exports = ( function ( mw ) {
+ 'use strict';
+
+ /**
+ * @callback wikibase.lexeme.widgets.RepresentationWidget.newComponent
+ *
+ * @param {{language:string, value: string}[]} representations
+ * @param {string|HTMLElement} element - ID selector or DOM node
+ * @param {string} template - template string or ID selector
+ * @param {function} beforeUpdate
+ *
+ * @return {object} Vue component object
+ */
+ function newComponent( representations, element, template, beforeUpdate
) {
+ return {
+ el: element,
+ template: template,
+
+ beforeUpdate: beforeUpdate,
+
+ data: {
+ inEditMode: false,
+ representations: representations
+ },
+ methods: {
+ edit: function () {
+ this.inEditMode = true;
+ if ( this.representations.length === 0
) {
+ this.add();
+ }
+ },
+ stopEditing: function () {
+ this.inEditMode = false;
+ },
+ add: function () {
+ if ( !this.inEditMode ) {
+ throw new Error( 'Cannot add
representation if not in edit mode' );
+ }
+ this.representations.push( { language:
'', value: '' } );
+ },
+ remove: function ( representation ) {
+ if ( !this.inEditMode ) {
+ throw new Error( 'Cannot remove
representation if not in edit mode' );
+ }
+ var index =
this.representations.indexOf( representation );
+ this.representations.splice( index, 1 );
+ }
+ },
+ filters: {
+ message: function ( key ) {
+ return mw.messages.get( key );
+ }
+ }
+ };
+ }
+
+ /**
+ * @callback wikibase.lexeme.widgets.RepresentationWidget.create
+ *
+ * @param {{language: string, value: string}[]} representations
+ * @param {string|HTMLElement} element - ID selector or DOM node
+ * @param {string} template - template string or ID selector
+ * @param {function} beforeUpdate
+ *
+ * @return {Vue} Initialized widget
+ */
+ function create( representations, element, template, beforeUpdate ) {
+ return new Vue( newComponent( representations, element,
template, beforeUpdate ) );
+ }
+
+ /**
+ * @class wikibase.lexeme.widgets.RepresentationWidget
+ */
+ return {
+ create: create
+ };
+
+} )( mediaWiki );
diff --git a/src/View/LexemeFormsView.php b/src/View/LexemeFormsView.php
index fe45c97..259e7bf 100644
--- a/src/View/LexemeFormsView.php
+++ b/src/View/LexemeFormsView.php
@@ -3,11 +3,13 @@
namespace Wikibase\Lexeme\View;
use Wikibase\DataModel\Entity\ItemId;
+use Wikibase\DataModel\Term\Term;
use Wikibase\Lexeme\DataModel\Form;
use Wikibase\Lexeme\View\Template\LexemeTemplateFactory;
use Wikibase\Lib\EntityIdHtmlLinkFormatter;
use Wikibase\View\LocalizedTextProvider;
use Wikibase\View\StatementSectionsView;
+use WMDE\VueJsTemplating\Templating;
/**
* @license GPL-2.0+
@@ -34,6 +36,11 @@
* @var StatementSectionsView
*/
private $statementSectionView;
+
+ /**
+ * @var string
+ */
+ private $languageCode;
public function __construct(
LocalizedTextProvider $textProvider,
@@ -66,6 +73,7 @@
}
$html .= '</div>';
$html .= '</div>';
+ $html .= $this->getRepresentationsVueTemplate();
return $html;
}
@@ -76,9 +84,6 @@
* @return string HTML
*/
private function getFormHtml( Form $form ) {
- //TODO Change to rendering all the representations
- $representation =
$form->getRepresentations()->getIterator()->current()->getText();
-
$grammaticalFeaturesHtml = $this->templateFactory->render(
'wikibase-lexeme-form-grammatical-features',
[ implode(
@@ -93,13 +98,42 @@
);
return $this->templateFactory->render( 'wikibase-lexeme-form', [
- 'some language',
- htmlspecialchars( $representation ),
- wfMessage( 'parentheses' )->rawParams(
htmlspecialchars( $form->getId()->getSerialization() ) )
- ->text(),
+ htmlspecialchars( $form->getId()->getSerialization() ),
+ $this->renderRepresentationWidget( $form ),
$grammaticalFeaturesHtml,
$this->statementSectionView->getHtml(
$form->getStatements() )
] );
+ }
+
+ /**
+ * @return string
+ */
+ private function renderRepresentationWidget( Form $form ) {
+ $templating = new Templating();
+
+ $representations = array_map(
+ function ( Term $r ) {
+ return [ 'value' => $r->getText(), 'language'
=> $r->getLanguageCode() ];
+ },
+ iterator_to_array( $form->getRepresentations() )
+ );
+
+ $result = $templating->render(
+ $this->getRawRepresentationVueTemplate(),
+ [
+ 'inEditMode' => false,
+ 'representations' => $representations
+ ],
+ [
+ 'message' => function ( $key ) {
+ return $this->textProvider->get( $key );
+ }
+ ]
+ );
+
+ return '<div class="form-representations">'
+ . $result
+ . '</div>';
}
/**
@@ -110,4 +144,49 @@
return $this->entityIdHtmlFormatter->formatEntityId( $id );
}
+ private function getRepresentationsVueTemplate() {
+ return <<<HTML
+<script id="representation-widget-vue-template" type="x-template">
+ {$this->getRawRepresentationVueTemplate()}
+</script>
+HTML;
+ }
+
+ private function getRawRepresentationVueTemplate() {
+ return <<<'HTML'
+<div class="representation-widget">
+ <ul v-if="!inEditMode"
class="representation-widget_representation-list">
+ <li v-for="representation in representations"
class="representation-widget_representation">
+ <span
class="representation-widget_representation-value">{{representation.value}}</span>
+ <span
class="representation-widget_representation-language">
+ {{representation.language}}
+ </span>
+ </li>
+ </ul>
+ <div v-else>
+ <div class="representation-widget_edit-area">
+ <ul class="representation-widget_representation-list">
+ <li v-for="representation in representations"
+
class="representation-widget_representation-edit-box">
+ <input size="1"
class="representation-widget_representation-value-input"
+ v-model="representation.value">
+ <input size="1"
class="representation-widget_representation-language-input"
+
v-model="representation.language">
+ <button
class="representation-widget_representation-remove"
+
v-on:click="remove(representation)"
+
:title="'wikibase-remove'|message">
+ ×
+ </button>
+ </li>
+ <li>
+ <button type="button"
class="representation-widget_add" v-on:click="add"
+
:title="'wikibase-add'|message">+</button>
+ </li>
+ </ul>
+ </div>
+ </div>
+</div>
+HTML;
+ }
+
}
diff --git a/src/WikibaseLexeme.hooks.php b/src/WikibaseLexeme.hooks.php
index b950c1e..06db641 100644
--- a/src/WikibaseLexeme.hooks.php
+++ b/src/WikibaseLexeme.hooks.php
@@ -112,6 +112,7 @@
'tests/qunit/widgets/LemmaWidgetStore.tests.js',
'tests/qunit/widgets/LemmaWidget.tests.js',
'tests/qunit/widgets/GlossWidget.tests.js',
+
'tests/qunit/widgets/RepresentationWidget.tests.js',
],
'dependencies' => [
'jquery.valueview.tests.testExpert',
@@ -133,6 +134,7 @@
'wikibase.lexeme.widgets.LemmaWidget.newLemmaWidgetStore',
'wikibase.lexeme.widgets.LemmaWidget.newLemmaWidget',
'wikibase.lexeme.widgets.GlossWidget',
+ 'wikibase.lexeme.widgets.RepresentationWidget',
'wikibase.tests.qunit.testrunner',
'vue',
'vuex',
diff --git a/tests/browser/features/forms.feature
b/tests/browser/features/forms.feature
index d15175a..e6f1de0 100644
--- a/tests/browser/features/forms.feature
+++ b/tests/browser/features/forms.feature
@@ -11,7 +11,7 @@
Then Forms header should be there
And Forms container should be there
And for each Form there is a representation and an ID
- And each representation is enclosed in tag having lang attribute with
"some language" as a value
+ And each representation has a language
@integration
Scenario: View Forms grammatical features
@@ -37,20 +37,23 @@
Then the first Form should no longer have the removed grammatical feature
@integration
- Scenario: Change representation
- Given I have a Lexeme with a Form
+ Scenario: Change multi-variant representations
+ Given I have a Lexeme with a Form
And I am on the page of the Lexeme to test
When I click on the first Form's edit button
- And I enter "new-representation" as the form representation
+ And I enter "colors" as the "en-us" form representation
+ And I click on the add representation button
+ And I enter "colours" as the "en-gb" form representation
And I save the Form
- Then "new-representation" should be displayed as a representation of the
Form
+ Then "colors" should be displayed as the "en-us" representation of the Form
+ And "colours" should be displayed as the "en-gb" representation of the
Form
@integration
Scenario: Add Form
When I click the Forms list add button
- And I enter "whatever" as the form representation
+ And I enter "whatever" as the "en" form representation
And I save the Form
- Then "whatever" should be displayed as a representation of the Form
+ Then "whatever" should be displayed as the "en" representation of the Form
@integration
@@ -64,7 +67,7 @@
Given I have the following properties with datatype:
| stringprop | string |
And I click the Forms list add button
- And I enter "newForm" as the form representation
+ And I enter "newForm" as the "en" form representation
And I save the Form
When I click add statement on the Form
And I select the claim property stringprop
diff --git a/tests/browser/features/step_definitions/forms_steps.rb
b/tests/browser/features/step_definitions/forms_steps.rb
index 446bb17..d561f20 100644
--- a/tests/browser/features/step_definitions/forms_steps.rb
+++ b/tests/browser/features/step_definitions/forms_steps.rb
@@ -10,11 +10,11 @@
expect(on(LexemePage).forms.count).to be > 0
end
-
Then(/^for each Form there is a representation and an ID$/) do
- #todo: this only checks if there is at least one id and representation
- expect(on(LexemePage).form_representation?).to be true
- expect(on(LexemePage).form_id?).to be true
+ on(LexemePage).forms.each do |form|
+ expect(form.representations.count).to be > 0
+ expect(form.id?).to be true
+ end
end
Then(/^for each Form there is a statement list$/) do
@@ -23,9 +23,12 @@
end
end
-Then(/^each representation is enclosed in tag having lang attribute with
"(.+)" as a value$/) do |value|
- #todo: this only checks if there is at least one lang attribute
- on(LexemePage).form_representation_element.attribute('lang').should == value
+Then(/^each representation has a language$/) do
+ on(LexemePage).forms.each do |form|
+ form.representations.each do |representation|
+ expect(representation.language?).to be true
+ end
+ end
end
Given(/^for each Form there is a grammatical feature list$/) do
@@ -39,9 +42,11 @@
@form_I_am_currently_editing = on(LexemePage).forms[-1]
end
-When(/^I enter "(.+)" as the form representation$/) do |representation|
- @form_I_am_currently_editing.representation_input_element.when_visible.clear
- @form_I_am_currently_editing.representation_input = representation
+When(/^I enter "(.*?)" as the "(.*?)" form representation$/) do
|representation, language|
+ last_representation = @form_I_am_currently_editing.representations[-1]
+
+ last_representation.value_input = representation
+ last_representation.language_input = language
end
When(/^I save the Form$/) do
@@ -49,8 +54,17 @@
@form_I_am_currently_editing.save_element.when_visible.click
end
-Then(/^"(.+)" should be displayed as a representation of the Form$/) do
|representation|
- @form_I_am_currently_editing.representation_element.text.should ==
representation
+Then(/^"(.*?)" should be displayed as the "(.*?)" representation of the
Form$/) do |value, language|
+ has_representation_with_value =
@form_I_am_currently_editing.representations.any? do |representation|
+ representation.value_element.when_visible.text == value
+ representation.language_element.when_visible.text == language
+ end
+
+ expect(has_representation_with_value).to be true
+end
+
+When(/^I click on the add representation button$/) do
+ @form_I_am_currently_editing.add_representation_element.when_visible.click
end
Given(/^I have a Lexeme with a Form$/) do
diff --git a/tests/browser/features/support/pages/lexeme_page.rb
b/tests/browser/features/support/pages/lexeme_page.rb
index 403f86b..335ad09 100644
--- a/tests/browser/features/support/pages/lexeme_page.rb
+++ b/tests/browser/features/support/pages/lexeme_page.rb
@@ -27,24 +27,38 @@
a(:delete_button, css: '.oo-ui-buttonElement > .oo-ui-buttonElement-button')
end
+class FormRepresentation
+ include PageObject
+
+ text_field(:value_input, class:
'representation-widget_representation-value-input')
+ text_field(:language_input, class:
'representation-widget_representation-language-input')
+ span(:value, class: 'representation-widget_representation-value')
+ span(:language, class: 'representation-widget_representation-language')
+end
+
class LexemeForm
include PageObject
- span(:representation, class: 'wikibase-lexeme-form-text')
+ div(:id, class: 'wikibase-lexeme-form-id')
div(:grammatical_feature_list, class:
'wikibase-lexeme-form-grammatical-features')
div(:statements, class: 'wikibase-statementgrouplistview')
- textarea(:representation_input, css: '.wikibase-lexeme-form-text > textarea')
text_field(:grammatical_features_input, css:
'.wikibase-lexeme-form-grammatical-features-values input')
a(:save, css: '.wikibase-toolbar-button-save > a')
a(:cancel, css: '.wikibase-toolbar-button-cancel > a')
a(:edit, css: '.wikibase-toolbar-button-edit > a')
a(:grammatical_feature_selection_first_option, css:
'.wikibase-lexeme-form-grammatical-features-values
.oo-ui-menuOptionWidget:first-of-type a')
+ button(:add_representation, class: 'representation-widget_add')
page_section(:statement_group, StatementGroup, class:
'wikibase-statementgrouplistview')
page_sections(
:grammatical_features,
GrammaticalFeatureValue,
css: '.wikibase-lexeme-form-grammatical-features-values > span,
.wikibase-lexeme-form-grammatical-features-values .oo-ui-tagItemWidget'
+ )
+ page_sections(
+ :representations,
+ FormRepresentation,
+ css: '.representation-widget_representation,
.representation-widget_representation-edit-box'
)
def grammatical_feature?(label)
@@ -93,7 +107,6 @@
span(:forms_header, id: 'forms')
div(:forms_container, class: 'wikibase-lexeme-forms')
h3(:form_representation, class: 'wikibase-lexeme-form-representation')
- span(:form_id, class: 'wikibase-lexeme-form-id')
span(:senses_header, id: 'senses')
div(:senses_container, class: 'wikibase-lexeme-senses')
diff --git a/tests/phpunit/mediawiki/View/LexemeFormsViewTest.php
b/tests/phpunit/mediawiki/View/LexemeFormsViewTest.php
index 2a31861..8c94a21 100644
--- a/tests/phpunit/mediawiki/View/LexemeFormsViewTest.php
+++ b/tests/phpunit/mediawiki/View/LexemeFormsViewTest.php
@@ -67,9 +67,32 @@
assertThat(
$html,
- is( htmlPiece( havingChild(
- both( tagMatchingOutline( '<h3 lang="some
language">' ) )
- ->andAlso( havingTextContents(
containsString( 'FORM_REPRESENTATION (F1)' ) ) )
+ is( htmlPiece(
+ both( havingChild(
+ allOf(
+ withClass(
'representation-widget_representation-value' ),
+ havingTextContents(
containsString( 'FORM_REPRESENTATION' ) )
+ ) ) )
+ ->andAlso( havingChild(
+ allOf(
+ withClass(
'representation-widget_representation-language' ),
+ havingTextContents(
containsString( 'en' ) )
+ )
+ ) ) ) )
+ );
+ }
+
+ public function testHtmlContainsFormId() {
+ $view = $this->newFormsView();
+ $html = $view->getHtml( [
+ NewForm::havingId( 'F1' )->build()
+ ] );
+
+ assertThat(
+ $html,
+ is( htmlPiece(
+ havingChild(
+ havingTextContents( containsString(
'F1' ) )
) ) )
);
}
@@ -106,7 +129,15 @@
return new LexemeFormsView(
new DummyLocalizedTextProvider(),
new LexemeTemplateFactory( [
- 'wikibase-lexeme-form' => '<h3 lang="$1">$2
$3</h3>$4 $5',
+ 'wikibase-lexeme-form' => '
+ <div class="wikibase-lexeme-form">
+ <div
class="wikibase-lexeme-form-header">
+ <div
class="wikibase-lexeme-form-id">$1</div>
+ <div
class="form-representations">$2</div>
+ </div>
+ $3
+ $4
+ </div>',
'wikibase-lexeme-form-grammatical-features' =>
'<div>$1</div>'
] ),
new EntityIdHtmlLinkFormatter(
diff --git a/tests/qunit/jquery.wikibase.lexemeformview.tests.js
b/tests/qunit/jquery.wikibase.lexemeformview.tests.js
index e029746..89656f1 100644
--- a/tests/qunit/jquery.wikibase.lexemeformview.tests.js
+++ b/tests/qunit/jquery.wikibase.lexemeformview.tests.js
@@ -4,24 +4,29 @@
( function ( $, wb, QUnit ) {
'use strict';
- var TEST_LEXMEFORMVIEW_CLASS = 'test_lexemeformview';
-
/** @type {wikibase.datamodel.TermMap}*/
var TermMap = wb.datamodel.TermMap;
/** @type {wikibase.datamodel.Term}*/
var Term = wb.datamodel.Term;
+ var selector = {
+ representationTextInput:
'.representation-widget_representation-value-input',
+ representationLanguageInput:
'.representation-widget_representation-language-input',
+ representationText:
'.representation-widget_representation-value'
+ };
+
QUnit.module( 'jquery.wikibase.lexemeformview', QUnit.newMwEnvironment(
{
- teardown: function () {
- $( '.' + TEST_LEXMEFORMVIEW_CLASS ).remove();
+ setup: function () {
+ $( '<script id="representation-widget-vue-template"
type="x-template"/>' )
+ .html( getRepresentationWidgetTemplate() )
+ .appendTo( '#qunit-fixture' );
}
} ) );
var newLexemeFormView = function ( options ) {
- var $node = $( '<div/>' ).appendTo( 'body' );
- options = options || {};
+ var $node = $( '<div/>' ).appendTo( '#qunit-fixture' );
- $node.addClass( TEST_LEXMEFORMVIEW_CLASS );
+ options = options || {};
options.api = options.api || {};
options.language = options.language || 'en';
@@ -32,7 +37,7 @@
};
options.buildStatementGroupListView = function () {};
- return $node.lexemeformview( options || {} ).data(
'lexemeformview' );
+ return $node.lexemeformview( options ).data( 'lexemeformview' );
};
var newForm = function ( id, defaultRepresentation ) {
@@ -66,13 +71,21 @@
} );
QUnit.test( 'value() creates value from input if it is in edit mode',
function ( assert ) {
+ var done = assert.async();
var view = newLexemeFormView(),
textInput = 'foobar';
- view.startEditing();
- view.element.find( view.options.inputNodeName ).val( textInput
);
+ view.startEditing().then( function () {
+ changeInputValue( view.element.find(
selector.representationLanguageInput ), 'en' );
+ changeInputValue( view.element.find(
selector.representationTextInput ), textInput );
- assert.equal( view.value().getRepresentations().getItemByKey(
'en' ).getText(), textInput );
+ assert.equal(
+ view.value().getRepresentations().getItemByKey(
'en' ).getText(),
+ textInput
+ );
+ } ).catch( function ( e ) {
+ assert.notOk( e.stack );
+ } ).then( done );
} );
QUnit.test( 'should not be in edit mode when initialized without a
value', function ( assert ) {
@@ -84,30 +97,88 @@
} );
QUnit.test( 'draws value in input node after startEditing()', function
( assert ) {
+ var done = assert.async();
var form = newForm( 'F123', 'foobar' ),
view = newLexemeFormView( {
value: form
} );
- view.startEditing();
- assert.equal(
- view.element.find( view.options.inputNodeName ).val(),
- form.getRepresentations().getItemByKey( 'en' ).getText()
- );
+ view.startEditing().then( function () {
+ assert.equal(
+ view.element.find(
selector.representationTextInput ).val(),
+ form.getRepresentations().getItemByKey( 'en'
).getText()
+ );
+ } ).catch( function ( e ) {
+ assert.notOk( e.stack );
+ } ).then( done );
+
} );
QUnit.test( 'draws value in text node after stopEditing()', function (
assert ) {
+ var done = assert.async();
+
var form = newForm( 'F123', 'foobar' ),
view = newLexemeFormView( {
value: form
} );
- view.startEditing();
- view.stopEditing();
- assert.equal(
- view.element.find( '.wikibase-lexeme-form-text'
).text(),
- form.getRepresentations().getItemByKey( 'en' ).getText()
- );
+ view.startEditing().then( function () {
+ return view.stopEditing();
+ } ).then( function () {
+ assert.equal(
+ view.element.find( selector.representationText
).text(),
+ form.getRepresentations().getItemByKey( 'en'
).getText()
+ );
+ } ).catch( function ( e ) {
+ assert.notOk( e.stack );
+ } ).then( done );
} );
+ /**
+ * Sets input value and triggers 'input'
+ * @param {jQuery}$element
+ * @param {string} newValue
+ */
+ function changeInputValue( $element, newValue ) {
+ $element.val( newValue );
+ var event = document.createEvent( 'Event' );
+ event.initEvent( 'input', true, true );
+ $element[ 0 ].dispatchEvent( event );
+ }
+
+ function getRepresentationWidgetTemplate() {
+ return '<div class="representation-widget">\n' +
+ '<ul v-if="!inEditMode"
class="representation-widget_representation-list">\n' +
+ '<li v-for="representation in representations"
class="representation-widget_representation">\n' +
+ '<span
class="representation-widget_representation-value">{{representation.value}}</span>\n'
+
+ '<span
class="representation-widget_representation-language">\n' +
+ '{{representation.language}}\n' +
+ '</span>\n' +
+ '</li>\n' +
+ '</ul>\n' +
+ '<div v-else>\n' +
+ '<div class="representation-widget_edit-area">\n' +
+ '<ul
class="representation-widget_representation-list">\n' +
+ '<li v-for="representation in representations" \n' +
+
'class="representation-widget_representation-edit-box">\n' +
+ '<input size="1"
class="representation-widget_representation-value-input" \n' +
+ 'v-model="representation.value">\n' +
+ '<input size="1"
class="representation-widget_representation-language-input" \n' +
+ 'v-model="representation.language">\n' +
+ '<button
class="representation-widget_representation-remove" \n' +
+ 'v-on:click="remove(representation)" \n' +
+ ':title="\'wikibase-remove\'|message">\n' +
+ '×\n' +
+ '</button>\n' +
+ '</li>\n' +
+ '<li>\n' +
+ '<button type="button"
class="representation-widget_add" v-on:click="add" \n' +
+ ':title="\'wikibase-add\'|message">+</button>\n' +
+ '</li>\n' +
+ '</ul>\n' +
+ '</div>\n' +
+ '</div>\n' +
+ '</div>';
+ }
+
}( jQuery, wikibase, QUnit ) );
diff --git a/tests/qunit/widgets/RepresentationWidget.tests.js
b/tests/qunit/widgets/RepresentationWidget.tests.js
new file mode 100644
index 0000000..854d233
--- /dev/null
+++ b/tests/qunit/widgets/RepresentationWidget.tests.js
@@ -0,0 +1,116 @@
+/**
+ * @license GPL-2.0+
+ */
+( function ( wb, $, QUnit ) {
+ 'use strict';
+
+ var RepresentationWidget = require(
'wikibase.lexeme.widgets.RepresentationWidget' );
+
+ var EMPTY_REPRESENTATION = { language: '', value: '' };
+ var SOME_REPRESENTATION = { language: 'en', value: 'representation in
english' };
+
+ QUnit.module(
+ 'wikibase.lexeme.widgets.RepresentationWidget',
+ function () {
+ QUnit.test(
+ 'widget without representations, start editing
- new empty representation is added',
+ function ( assert ) {
+ var widget = newWidget( [] );
+
+ widget.edit();
+
+ assert.deepEqual(
widget.representations, [ EMPTY_REPRESENTATION ] );
+ }
+ );
+
+ QUnit.test(
+ 'widget with representation, start editing -
only initial representation present',
+ function ( assert ) {
+ var widget = newWidget( [
SOME_REPRESENTATION ] );
+
+ widget.edit();
+
+ assert.deepEqual(
+ widget.representations,
+ [ SOME_REPRESENTATION ]
+ );
+ }
+ );
+
+ QUnit.test( 'when created - not in edit mode', function
( assert ) {
+ var widget = newWidget( [] );
+
+ assert.notOk( widget.inEditMode );
+ } );
+
+ QUnit.test( 'switch to edit mode', function ( assert ) {
+ var widget = newWidget( [] );
+
+ widget.edit();
+
+ assert.ok( widget.inEditMode );
+ } );
+
+ QUnit.test( 'not in edit mode after stopEditing was
called', function ( assert ) {
+ var widget = newWidget( [] );
+
+ widget.edit();
+ widget.stopEditing();
+
+ assert.notOk( widget.inEditMode );
+ } );
+
+ QUnit.test( 'add a representation - empty
representation added', function ( assert ) {
+ var widget = newWidget(
+ [ SOME_REPRESENTATION ]
+ );
+
+ widget.edit();
+ widget.add();
+
+ assert.deepEqual( widget.representations[ 1 ],
EMPTY_REPRESENTATION );
+ } );
+
+ QUnit.test( 'remove a representation', function (
assert ) {
+ var widget = newWidget(
+ [ SOME_REPRESENTATION ]
+ );
+
+ widget.edit();
+ widget.remove( SOME_REPRESENTATION );
+
+ assert.deepEqual( widget.representations, [] );
+ } );
+
+ QUnit.test( 'cannot add representation if not in edit
mode', function ( assert ) {
+ var widget = newWidget( [] );
+
+ assert.throws( function () {
+ widget.add();
+ }, Error );
+ } );
+
+ QUnit.test( 'cannot remove representation if not in
edit mode', function ( assert ) {
+ var widget = newWidget(
+ [ SOME_REPRESENTATION ]
+ );
+
+ assert.throws( function () {
+ widget.remove( SOME_REPRESENTATION );
+ }, Error );
+ } );
+
+ }
+ );
+
+ function newWidget( representations ) {
+ return RepresentationWidget.create(
+ representations,
+ document.createElement( 'div' ),
+ '<div id="dummy-template"></div>',
+ function () {
+ }
+ );
+ }
+
+}( wikibase, jQuery, QUnit ) );
--
To view, visit https://gerrit.wikimedia.org/r/362180
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I5dda249fb2121db3b29804e202b9f90d3755a968
Gerrit-PatchSet: 19
Gerrit-Project: mediawiki/extensions/WikibaseLexeme
Gerrit-Branch: master
Gerrit-Owner: Jakob <[email protected]>
Gerrit-Reviewer: Aleksey Bekh-Ivanov (WMDE) <[email protected]>
Gerrit-Reviewer: Jonas Kress (WMDE) <[email protected]>
Gerrit-Reviewer: WMDE-leszek <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits