Pablo Grass (WMDE) has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/370642 )

Change subject: SearchPreview: Show truncated search values
......................................................................

SearchPreview: Show truncated search values

Search values are, to a point, shown inside search preview.
Complex values (multiple, with comparison operator) get fromatted.
Full formatted search value is shown in title on mouse-over.
Also removes draggable behavior from preview pills.

Issue: T171756
Change-Id: Ia7adaba829e958f597cf33684671405836078908
---
M AdvancedSearch.hooks.php
M modules/ext.advancedSearch.css
M modules/ui/ext.advancedSearch.ExpandablePane.js
M modules/ui/ext.advancedSearch.SearchPreview.js
M package-lock.json
M package.json
A tests/qunit/.eslintrc.json
A tests/qunit/ui/SearchPreview.test.js
8 files changed, 309 insertions(+), 41 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/AdvancedSearch 
refs/changes/42/370642/1

diff --git a/AdvancedSearch.hooks.php b/AdvancedSearch.hooks.php
index a6214cc..98c6c95 100644
--- a/AdvancedSearch.hooks.php
+++ b/AdvancedSearch.hooks.php
@@ -78,11 +78,13 @@
                        'scripts' => [
                                'tests/qunit/ui/NamespaceFilters.test.js',
                                'tests/qunit/ui/NamespacePresets.test.js',
+                               'tests/qunit/ui/SearchPreview.test.js',
                                'tests/qunit/dm/SearchModel.test.js'
                        ],
                        'dependencies' => [
                                'ext.advancedSearch.ui.NamespaceFilters',
                                'ext.advancedSearch.ui.NamespacePresets',
+                               'ext.advancedSearch.ui.SearchPreview',
                                'ext.advancedSearch.dm.SearchModel',
                                'oojs-ui'
                        ],
diff --git a/modules/ext.advancedSearch.css b/modules/ext.advancedSearch.css
index 1973f21..57d7265 100644
--- a/modules/ext.advancedSearch.css
+++ b/modules/ext.advancedSearch.css
@@ -1,3 +1,31 @@
+.mw-advancedSearch-searchPreview-label {
+       font-weight: bold;
+       margin-right: 0.3125em;
+}
+
+.mw-advancedSearch-searchPreview-previewPill {
+       display: inline-flex;
+       display: -ms-inline-flexbox;
+       flex-flow: row nowrap;
+       -ms-flex-direction: row;
+       flex: 0 1 auto;
+}
+
+.mw-advancedSearch-searchPreview-previewPill > .oo-ui-labelElement-label {
+       order: 1;
+       -ms-flex-order: 1;
+}
+
+.mw-advancedSearch-searchPreview-previewPill > .content {
+       display: inline-block;
+       text-overflow: ellipsis;
+       overflow: hidden;
+       max-width: 5em;
+       order: 2;
+       -ms-flex-order: 2;
+       padding-left: 0.3em;
+}
+
 .mw-advancedSearch-fieldContainer {
        background: linear-gradient( rgba( 0, 0, 0, 0.1 ), #fff 0.5em );
        border: solid 1px #c8ccd1;
@@ -69,15 +97,6 @@
        top: 0;
        height: 100%;
        right: 0.9375em;
-}
-
-.mw-advancedSearch-searchPreview-label {
-       font-weight: bold;
-       margin-right: 0.3125em;
-}
-
-.mw-advancedSearch-previewLabel.oo-ui.widget-enabled {
-       background: #fff;
 }
 
 .mw-advancedSearch-optionTags {
diff --git a/modules/ui/ext.advancedSearch.ExpandablePane.js 
b/modules/ui/ext.advancedSearch.ExpandablePane.js
index e3d5938..074948b 100644
--- a/modules/ui/ext.advancedSearch.ExpandablePane.js
+++ b/modules/ui/ext.advancedSearch.ExpandablePane.js
@@ -50,27 +50,6 @@
                        this.$dependentPane.append( config.$paneContent );
                }
 
-               // TODO Move the following code to a separate widget
-               /*
-               var $labelContainer = $( '<div><strong>Advanced 
Parameters</strong></div>' );
-               $bar.append( $labelContainer );
-
-               var dummyDemoWidgets = [
-                       new OO.ui.TagItemWidget( { label: 'First demo label: 
foo' } ),
-                       new OO.ui.TagItemWidget( { label: 'Second demo label: 
bar' } ),
-                       new OO.ui.TagItemWidget( { label: 'Third demo label: 
baz' } ),
-                       new OO.ui.TagItemWidget( { label: 'Fourth demo label: 
quux' } ),
-                       new OO.ui.TagItemWidget( { label: 'Fivth demo label: 
quuz' } )
-               ];
-
-               $.each( dummyDemoWidgets, function ( _, w ) {
-                       // TODO Create custom TagItemWidget classes that have 
special classes (for styling) and disable clicking
-                       w.$element.on( 'click', function () { return false; } );
-                       w.$element.addClass( 'mw-advancedSearch-previewLabel' );
-                       $labelContainer.append( w.$element );
-               } );
-               */
-
                this.$btn.addClass( 'oo-ui-buttonElement-framed' );
                this.$element.addClass( 'mw-advancedSearch-expandablePane' );
                this.$element.append( this.$btn, this.$dependentPane );
diff --git a/modules/ui/ext.advancedSearch.SearchPreview.js 
b/modules/ui/ext.advancedSearch.SearchPreview.js
index adc7e01..9bf8172 100644
--- a/modules/ui/ext.advancedSearch.SearchPreview.js
+++ b/modules/ui/ext.advancedSearch.SearchPreview.js
@@ -41,29 +41,89 @@
                this.updatePreview();
        };
 
+       /**
+        * Render the preview for all options
+        */
        mw.libs.advancedSearch.ui.SearchPreview.prototype.updatePreview = 
function () {
-               var self = this;
                // TODO check if we really need to re-generate
                this.$element.find( 
'.mw-advancedSearch-searchPreview-previewPill' ).remove();
 
                if ( !this.data ) {
                        return;
                }
+
                $.each( this.previewOptions, function ( optionId, option ) {
-                       var val = self.store.getOption( optionId );
-                       if ( !val || ( optionId.match( '^file[hw]$' ) && ( 
!$.isArray( val ) || !val[ 1 ] ) ) ) {
+                       var val = this.store.getOption( optionId );
+
+                       if ( this.skipOptionInPreview( optionId, val ) ) {
                                return;
                        }
-                       var tag = new OO.ui.TagItemWidget( { label: 
option.label } );
-                       tag.connect( this, {
-                               remove: function () {
-                                       self.store.removeOption( optionId );
-                               }
+
+                       this.$element.append( this.generateTag( optionId, 
option, val ).$element );
+               }.bind( this ) );
+       };
+
+       /**
+        * Decide if an option-value-combination should be listed in the preview
+        *
+        * @param {string} optionId
+        * @param {string|array} value
+        * @return {boolean}
+        */
+       mw.libs.advancedSearch.ui.SearchPreview.prototype.skipOptionInPreview = 
function ( optionId, value ) {
+               return Boolean( !value || ( optionId.match( '^file[hw]$' ) && ( 
!$.isArray( value ) || !value[ 1 ] ) ) );
+       };
+
+       /**
+        * Create a tag item that represents the preview for a single 
option-value-combination
+        *
+        * @param {string} optionId
+        * @param {object} option
+        * @param {string} value
+        * @return {OO.ui.TagItemWidget}
+        */
+       mw.libs.advancedSearch.ui.SearchPreview.prototype.generateTag = 
function ( optionId, option, value ) {
+               var formattedValue = this.formatValue( value ),
+                       tag = new OO.ui.TagItemWidget( {
+                               label: option.label,
+                               content: [
+                                       new OO.ui.HtmlSnippet(
+                                               $( '<span>' )   // redundant 
span to cover browsers without support for bdi tag
+                                                       .addClass( 'content' )
+                                                       .append(
+                                                               $( '<bdi>' 
).text( formattedValue )
+                                                       )
+                                       )
+                               ]
                        } );
-                       tag.$label.attr( 'title', option.formatter( val ) );
-                       tag.$element.addClass( 
'mw-advancedSearch-searchPreview-previewPill' );
-                       self.$element.append( tag.$element );
+
+               tag.toggleDraggable( false ); // constructor config has no 
effect; https://phabricator.wikimedia.org/T172781
+
+               tag.connect( this, {
+                       remove: function () {
+                               self.store.removeOption( optionId );
+                       }
                } );
+
+               tag.$element
+                       .attr( 'title', formattedValue )
+                       .addClass( 
'mw-advancedSearch-searchPreview-previewPill' );
+
+               return tag;
+       };
+
+       /**
+        * Format a value to be used in the preview
+        *
+        * @param {string|array} value
+        * @return {string}
+        */
+       mw.libs.advancedSearch.ui.SearchPreview.prototype.formatValue = 
function ( value ) {
+               if ( $.isArray( value ) ) {
+                       value = value.join( ' ' );
+               }
+
+               return value.trim();
        };
 
        mw.libs.advancedSearch.ui.SearchPreview.prototype.showPreview = 
function () {
diff --git a/package-lock.json b/package-lock.json
index 1aed77c..41c606b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -669,6 +669,12 @@
                        "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
                        "dev": true
                },
+               "formatio": {
+                       "version": "1.1.1",
+                       "resolved": 
"https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz";,
+                       "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=",
+                       "dev": true
+               },
                "fs.realpath": {
                        "version": "1.0.0",
                        "resolved": 
"https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz";,
@@ -1259,6 +1265,12 @@
                        "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
                        "dev": true
                },
+               "lolex": {
+                       "version": "1.3.2",
+                       "resolved": 
"https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz";,
+                       "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=",
+                       "dev": true
+               },
                "loud-rejection": {
                        "version": "1.6.0",
                        "resolved": 
"https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz";,
@@ -1761,6 +1773,12 @@
                        "integrity": 
"sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
                        "dev": true
                },
+               "samsam": {
+                       "version": "1.1.2",
+                       "resolved": 
"https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz";,
+                       "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=",
+                       "dev": true
+               },
                "semver": {
                        "version": "5.3.0",
                        "resolved": 
"https://registry.npmjs.org/semver/-/semver-5.3.0.tgz";,
@@ -1777,6 +1795,12 @@
                        "version": "3.0.2",
                        "resolved": 
"https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz";,
                        "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+                       "dev": true
+               },
+               "sinon": {
+                       "version": "1.17.3",
+                       "resolved": 
"https://registry.npmjs.org/sinon/-/sinon-1.17.3.tgz";,
+                       "integrity": "sha1-RNZLx0jQI4gARsFUPO/Oo0xH0X4=",
                        "dev": true
                },
                "slice-ansi": {
@@ -2105,6 +2129,20 @@
                        "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=",
                        "dev": true
                },
+               "util": {
+                       "version": "0.10.3",
+                       "resolved": 
"https://registry.npmjs.org/util/-/util-0.10.3.tgz";,
+                       "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+                       "dev": true,
+                       "dependencies": {
+                               "inherits": {
+                                       "version": "2.0.1",
+                                       "resolved": 
"https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz";,
+                                       "integrity": 
"sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+                                       "dev": true
+                               }
+                       }
+               },
                "util-deprecate": {
                        "version": "1.0.2",
                        "resolved": 
"https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz";,
diff --git a/package.json b/package.json
index 7671e6f..40a3d18 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
                "grunt-eslint": "19.0.0",
                "grunt-jsonlint": "1.1.0",
                "grunt-stylelint": "0.7.0",
+               "sinon": "^1.17.3",
                "stylelint-config-wikimedia": "0.4.1"
        }
 }
diff --git a/tests/qunit/.eslintrc.json b/tests/qunit/.eslintrc.json
new file mode 100644
index 0000000..a974377
--- /dev/null
+++ b/tests/qunit/.eslintrc.json
@@ -0,0 +1,9 @@
+{
+       "extends": "../../.eslintrc.json",
+       "env": {
+               "qunit": true
+       },
+       "globals": {
+               "sinon": false
+       }
+}
diff --git a/tests/qunit/ui/SearchPreview.test.js 
b/tests/qunit/ui/SearchPreview.test.js
new file mode 100644
index 0000000..cf4e687
--- /dev/null
+++ b/tests/qunit/ui/SearchPreview.test.js
@@ -0,0 +1,160 @@
+( function ( $, QUnit, sinon, mw ) {
+       'use strict';
+
+       var SearchPreview = mw.libs.advancedSearch.ui.SearchPreview,
+               store = {
+                       connect: function () {},
+                       getOption: function () {}
+               },
+               config = {};
+
+       QUnit.module( 'ext.advancedSearch.ui.SearchPreview' );
+
+       QUnit.test( 'Label gets setup', 1, function ( assert ) {
+               config = {
+                       label: 'something'
+               };
+               var searchPreview = new SearchPreview( store, config );
+
+               assert.equal( 'something', searchPreview.label.getLabel() );
+       } );
+
+       QUnit.test( 'Store data subscribed to and synced initially', 3, 
function ( assert ) {
+               store.connect = sinon.stub();
+               var updatePreviewSpy = sinon.spy( SearchPreview.prototype, 
'updatePreview' );
+
+               var searchPreview = new SearchPreview( store, config );
+
+               assert.ok( searchPreview );
+               assert.ok( store.connect.calledOnce );
+               assert.ok( updatePreviewSpy.calledOnce );
+       } );
+
+       QUnit.test( 'Store state is reflected in preview', 9, function ( assert 
) {
+               var generateTagSpy = sinon.spy( SearchPreview.prototype, 
'generateTag' );
+
+               store.getOption = sinon.stub();
+               store.getOption.withArgs( 'somename' ).returns( 'field one 
value' );
+               store.getOption.withArgs( 'another' ).returns( 'field two 
value' );
+
+               config.previewOptions = {
+                       somename: {
+                               label: 'my label:'
+                       },
+                       another: {
+                               label: 'somethingsomething:'
+                       }
+               };
+               var searchPreview = new SearchPreview( store, config );
+
+               var pills = $( '.mw-advancedSearch-searchPreview-previewPill', 
searchPreview.$element );
+               assert.equal( pills.length, 2 );
+
+               assert.ok( store.getOption.calledTwice );
+               assert.ok( generateTagSpy.calledTwice );
+
+               assert.equal( 'somename', generateTagSpy.firstCall.args[ 0 ] );
+               assert.equal( 'my label:', generateTagSpy.firstCall.args[ 1 
].label );
+               assert.equal( 'field one value', generateTagSpy.firstCall.args[ 
2 ] );
+
+               assert.equal( 'another', generateTagSpy.secondCall.args[ 0 ] );
+               assert.equal( 'somethingsomething:', 
generateTagSpy.secondCall.args[ 1 ].label );
+               assert.equal( 'field two value', 
generateTagSpy.secondCall.args[ 2 ] );
+       } );
+
+       QUnit.test( 'Options are correctly selected for preview', 14, function 
( assert ) {
+               var searchPreview = new SearchPreview( store, config );
+
+               assert.notOk( searchPreview.skipOptionInPreview( 'plain', 
'searchme' ) );
+               assert.notOk( searchPreview.skipOptionInPreview( 'filetype', 
'bitmap' ) );
+               assert.notOk( searchPreview.skipOptionInPreview( 'fileh', [ 
'<', '30' ] ) );
+               assert.notOk( searchPreview.skipOptionInPreview( 'filew', [ 
'>', '1400' ] ) );
+
+               assert.ok( searchPreview.skipOptionInPreview( 'plain', '' ) );
+               assert.ok( searchPreview.skipOptionInPreview( 'plain', null ) );
+               assert.ok( searchPreview.skipOptionInPreview( 'plain', null ) );
+               assert.ok( searchPreview.skipOptionInPreview( '', null ) );
+
+               assert.ok( searchPreview.skipOptionInPreview( 'fileh', null ) );
+               assert.ok( searchPreview.skipOptionInPreview( 'fileh', [] ) );
+               assert.ok( searchPreview.skipOptionInPreview( 'fileh', [ '>', 
null ] ) );
+               assert.ok( searchPreview.skipOptionInPreview( 'filew', null ) );
+               assert.ok( searchPreview.skipOptionInPreview( 'filew', [] ) );
+               assert.ok( searchPreview.skipOptionInPreview( 'filew', [ '>', 
null ] ) );
+       } );
+
+       QUnit.test( 'Tag is generated', 4, function ( assert ) {
+               var searchPreview = new SearchPreview( store, config );
+               var tag = searchPreview.generateTag(
+                       'somename',
+                       {
+                               label: 'my label:',
+                               formatter: function () {
+                                       assert.ok( false, 'formatter should not 
be used to display preview' );
+                               }
+                       },
+                       'my field value'
+               );
+
+               var element = tag.$element[ 0 ];
+
+               assert.equal( element.title, 'my field value' );
+               // https://phabricator.wikimedia.org/T172781 prevents a 
semantic way to check for draggable
+               assert.ok( $( element ).hasClass( 
'oo-ui-draggableElement-undraggable' ) );
+               assert.equal( $( '.content', element ).html(), '<bdi>my field 
value</bdi>' );
+               assert.equal( $( '.oo-ui-labelElement-label', element ).html(), 
'my label:' );
+       } );
+
+       QUnit.test( 'Showing renders pills', 1, function ( assert ) {
+               config.previewOptions = {
+                       one: {
+                               label: '1:'
+                       },
+                       two: {
+                               label: '2:'
+                       }
+               };
+
+               store.getOption = sinon.stub();
+               store.getOption.withArgs( 'one' ).returns( 'field one value' );
+               store.getOption.withArgs( 'two' ).returns( 'field two value' );
+
+               var searchPreview = new SearchPreview( store, config );
+               searchPreview.showPreview();
+
+               assert.equal( searchPreview.$element.find( 
'.mw-advancedSearch-searchPreview-previewPill' ).length, 2 );
+       } );
+
+       QUnit.test( 'Hiding removes pills', 1, function ( assert ) {
+               config.previewOptions = {
+                       one: {
+                               label: '1:'
+                       },
+                       two: {
+                               label: '2:'
+                       }
+               };
+
+               var searchPreview = new SearchPreview( store, config );
+               searchPreview.hidePreview();
+
+               assert.equal( searchPreview.$element.find( 
'.mw-advancedSearch-searchPreview-previewPill' ).length, 0 );
+       } );
+
+       QUnit.test( 'Tag value is well-formatted', 9, function ( assert ) {
+               var searchPreview = new SearchPreview( store, config );
+
+               assert.equal( searchPreview.formatValue( '' ), '' );
+               assert.equal( searchPreview.formatValue( 'hello' ), 'hello' );
+               assert.equal( searchPreview.formatValue( ' stray whitespace  ' 
), 'stray whitespace' );
+
+               assert.equal( searchPreview.formatValue( [ '', '' ] ), '' );
+               assert.equal( searchPreview.formatValue( [ '', 1000 ] ), '1000' 
);
+               assert.equal( searchPreview.formatValue( [ '>', 300 ] ), '> 
300' );
+               assert.equal( searchPreview.formatValue( [ '<', 1400 ] ), '< 
1400' );
+
+               assert.equal( searchPreview.formatValue( [ 'some', 'words', 
'in', 'combination' ] ), 'some words in combination' );
+               assert.equal( searchPreview.formatValue( [ '', ' stray', 
'whitespace  ' ] ), 'stray whitespace' );
+       } );
+
+}( jQuery, QUnit, sinon, mediaWiki ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/370642
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia7adaba829e958f597cf33684671405836078908
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/AdvancedSearch
Gerrit-Branch: master
Gerrit-Owner: Pablo Grass (WMDE) <pablo.gr...@wikimedia.de>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to